All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.seedstack.scheduler.internal.SchedulerPlugin Maven / Gradle / Ivy

/**
 * Copyright (c) 2013-2016, The SeedStack authors 
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package org.seedstack.scheduler.internal;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import io.nuun.kernel.api.plugin.InitState;
import io.nuun.kernel.api.plugin.context.Context;
import io.nuun.kernel.api.plugin.context.InitContext;
import io.nuun.kernel.api.plugin.request.ClasspathScanRequest;
import org.apache.commons.lang.StringUtils;
import org.kametic.specifications.Specification;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;
import org.seedstack.scheduler.Scheduled;
import org.seedstack.scheduler.ScheduledTasks;
import org.seedstack.scheduler.Task;
import org.seedstack.scheduler.TaskListener;
import org.seedstack.seed.SeedException;
import org.seedstack.seed.core.internal.AbstractSeedPlugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;

/**
 * Plugin for Quartz scheduler integration.
 */
public class SchedulerPlugin extends AbstractSeedPlugin {
    private static final Logger LOGGER = LoggerFactory.getLogger(SchedulerPlugin.class);
    private Specification> specificationForJobs;
    private Specification> specificationForJobListeners;
    private Collection> jobClasses;
    private Multimap, Class> jobListenerMap = ArrayListMultimap.create();
    private Scheduler scheduler;

    @Inject
    private static DelegateJobListener delegateJobListener;
    @Inject
    private static GuiceTaskFactory guiceTaskFactory;
    @Inject
    private static ScheduledTasks scheduledTasks;

    @Override
    public String name() {
        return "scheduler";
    }

    @Override
    public Collection classpathScanRequests() {
        specificationForJobs = classImplements(Task.class);
        specificationForJobListeners = classImplements(TaskListener.class);
        return classpathScanRequestBuilder().specification(specificationForJobs).specification(specificationForJobListeners).build();
    }


    @Override
    public InitState initialize(InitContext initContext) {
        Map>> scannedTypesBySpecification = initContext.scannedTypesBySpecification();

        // Associates - scan for nativeUnitModule
        jobClasses = scannedTypesBySpecification.get(specificationForJobs);

        Collection> listenerClasses = scannedTypesBySpecification.get(specificationForJobListeners);
        for (Class listenerClass : listenerClasses) {
            if (TaskListener.class.isAssignableFrom(listenerClass)) {
                // Get the type of Job to listen
                Type typeVariable = getParametrizedTypeOfJobListener(listenerClass);
                if (typeVariable != null && Task.class.isAssignableFrom((Class) typeVariable)) {
                    // bind the Task to the listener
                    //noinspection unchecked
                    jobListenerMap.put((Class) typeVariable, (Class) listenerClass);
                }
            }
        }

        // Initialises the scheduler and adds jobs
        StdSchedulerFactory schedulerFactory = new StdSchedulerFactory();
        try {
            this.scheduler = schedulerFactory.getScheduler();
        } catch (Exception e) {
            throw SeedException.wrap(e, SchedulerErrorCode.SCHEDULER_ERROR);
        }

        return InitState.INITIALIZED;
    }


    /**
     * Returns the type parameter of the TaskListener interface.
     *
     * @param listenerClass class to check
     * @return type which extends Task
     * @throws SeedException if no parameter type is present
     */
    private Type getParametrizedTypeOfJobListener(Class listenerClass) {
        Type[] interfaces = listenerClass.getGenericInterfaces();
        Type[] typeParameters = null;
        for (Type anInterface : interfaces) {
            // Checks if the class get parameters
            if (!(anInterface instanceof ParameterizedType)) {
                continue;
            }
            // Gets rawType to check if the interface is TaskListener
            Class interfaceClass = (Class) ((ParameterizedType) anInterface).getRawType();
            if (TaskListener.class.isAssignableFrom(interfaceClass)) {
                typeParameters = ((ParameterizedType) anInterface).getActualTypeArguments();
                break;
            }
        }
        if (typeParameters == null || typeParameters.length == 0) {
            throw SeedException.createNew(SchedulerErrorCode.MISSING_TYPE_PARAMETER)
                    .put("class", listenerClass);
        }

        return typeParameters[0];
    }

    @Override
    public Object nativeUnitModule() {
        return new SchedulerModule(jobClasses, scheduler, jobListenerMap);
    }

    @Override
    public void start(Context context) {
        super.start(context);

        try {
            // Configure scheduler
            scheduler.setJobFactory(guiceTaskFactory);
            scheduler.getListenerManager().addJobListener(delegateJobListener);

            // Schedule declarative tasks (@Scheduled)
            scheduleAnnotatedTasks();

            // Start scheduler
            scheduler.start();
        } catch (Exception e) {
            throw SeedException.wrap(e, SchedulerErrorCode.SCHEDULER_FAILED_TO_START);
        }
    }

    private void scheduleAnnotatedTasks() {
        try {
            for (Class candidateClass : jobClasses) {
                if (Task.class.isAssignableFrom(candidateClass)) {
                    Scheduled annotation = candidateClass.getAnnotation(Scheduled.class);
                    if (annotation != null && StringUtils.isNotBlank(annotation.value())) {
                        //noinspection unchecked
                        Class taskClass = (Class) candidateClass;
                        scheduledTasks.scheduledTask(taskClass).schedule();
                    }
                }
            }
        } catch (Exception e) {
            throw SeedException.wrap(e, SchedulerErrorCode.SCHEDULER_ERROR);
        }
    }

    @Override
    public void stop() {
        try {
            if (this.scheduler != null) {
                this.scheduler.shutdown();
            }
        } catch (SchedulerException e) {
            LOGGER.warn("Quartz scheduler failed to shutdown properly", e);
        }
        super.stop();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy