org.axonframework.eventhandling.scheduling.java.SimpleEventScheduler.java Source code

Java tutorial

Introduction

Here is the source code for org.axonframework.eventhandling.scheduling.java.SimpleEventScheduler.java

Source

/*
 * Copyright (c) 2010-2014. Axon Framework
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.axonframework.eventhandling.scheduling.java;

import org.axonframework.common.Assert;
import org.axonframework.domain.EventMessage;
import org.axonframework.domain.GenericEventMessage;
import org.axonframework.domain.IdentifierFactory;
import org.axonframework.eventhandling.EventBus;
import org.axonframework.eventhandling.scheduling.EventScheduler;
import org.axonframework.eventhandling.scheduling.ScheduleToken;
import org.axonframework.unitofwork.DefaultUnitOfWorkFactory;
import org.axonframework.unitofwork.UnitOfWork;
import org.axonframework.unitofwork.UnitOfWorkFactory;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * An {@link EventScheduler} implementation that uses Java's ScheduledExecutorService as scheduling and triggering
 * mechanism.
 * <p/>
 * Note that this mechanism is non-persistent. Scheduled tasks will be lost when the JVM is shut down, unless special
 * measures have been taken to prevent that. For more flexible and powerful scheduling options, see {@link
 * org.axonframework.eventhandling.scheduling.quartz.QuartzEventScheduler}.
 *
 * @author Allard Buijze
 * @see org.axonframework.eventhandling.scheduling.quartz.QuartzEventScheduler
 * @since 0.7
 */
public class SimpleEventScheduler implements EventScheduler {

    private static final Logger logger = LoggerFactory.getLogger(SimpleEventScheduler.class);

    private final ScheduledExecutorService executorService;
    private final EventBus eventBus;
    private final UnitOfWorkFactory unitOfWorkFactory;
    private final Map<String, Future<?>> tokens = new ConcurrentHashMap<String, Future<?>>();

    /**
     * Initialize the SimpleEventScheduler using the given <code>executorService</code> as trigger and execution
     * mechanism, and publishes events to the given <code>eventBus</code>.
     *
     * @param executorService The backing ScheduledExecutorService
     * @param eventBus        The Event Bus on which Events are to be published
     */
    public SimpleEventScheduler(ScheduledExecutorService executorService, EventBus eventBus) {
        this(executorService, eventBus, new DefaultUnitOfWorkFactory());
    }

    /**
     * Initialize the SimpleEventScheduler using the given <code>executorService</code> as trigger and execution
     * mechanism, and publishes events to the given <code>eventBus</code>. The <code>eventTriggerCallback</code> is
     * invoked just before and after publication of a scheduled event.
     *
     * @param executorService   The backing ScheduledExecutorService
     * @param eventBus          The Event Bus on which Events are to be published
     * @param unitOfWorkFactory The factory that creates the Unit of Work to manage transactions
     */
    public SimpleEventScheduler(ScheduledExecutorService executorService, EventBus eventBus,
            UnitOfWorkFactory unitOfWorkFactory) {
        Assert.notNull(executorService, "executorService may not be null");
        Assert.notNull(eventBus, "eventBus may not be null");
        Assert.notNull(unitOfWorkFactory, "unitOfWorkFactory may not be null");

        this.executorService = executorService;
        this.eventBus = eventBus;
        this.unitOfWorkFactory = unitOfWorkFactory;
    }

    @Override
    public ScheduleToken schedule(DateTime triggerDateTime, Object event) {
        return schedule(new Duration(null, triggerDateTime), event);
    }

    @Override
    public ScheduleToken schedule(Duration triggerDuration, Object event) {
        String tokenId = IdentifierFactory.getInstance().generateIdentifier();
        ScheduledFuture<?> future = executorService.schedule(new PublishEventTask(event, tokenId),
                triggerDuration.getMillis(), TimeUnit.MILLISECONDS);
        tokens.put(tokenId, future);
        return new SimpleScheduleToken(tokenId);
    }

    @Override
    public void cancelSchedule(ScheduleToken scheduleToken) {
        if (!SimpleScheduleToken.class.isInstance(scheduleToken)) {
            throw new IllegalArgumentException("The given ScheduleToken was not provided by this scheduler.");
        }
        Future<?> future = tokens.remove(((SimpleScheduleToken) scheduleToken).getTokenId());
        if (future != null) {
            future.cancel(false);
        }
    }

    private class PublishEventTask implements Runnable {

        private final Object event;
        private final String tokenId;

        public PublishEventTask(Object event, String tokenId) {
            this.event = event;
            this.tokenId = tokenId;
        }

        @SuppressWarnings("unchecked")
        @Override
        public void run() {
            EventMessage<?> eventMessage = createMessage();
            if (logger.isInfoEnabled()) {
                logger.info("Triggered the publication of event [{}]",
                        eventMessage.getPayloadType().getSimpleName());
            }
            UnitOfWork unitOfWork = unitOfWorkFactory.createUnitOfWork();
            try {
                unitOfWork.publishEvent(eventMessage, eventBus);
                unitOfWork.commit();
            } finally {
                tokens.remove(tokenId);
            }
        }

        /**
         * Creates a new message for the scheduled event. This ensures that a new identifier and timestamp will always
         * be generated, so that the timestamp will reflect the actual moment the trigger occurred.
         *
         * @return the message to publish
         */
        private EventMessage<?> createMessage() {
            EventMessage<?> eventMessage;
            if (event instanceof EventMessage) {
                eventMessage = new GenericEventMessage<Object>(((EventMessage) event).getPayload(),
                        ((EventMessage) event).getMetaData());
            } else {
                eventMessage = new GenericEventMessage<Object>(event);
            }
            return eventMessage;
        }
    }
}