Package org.apache.deltaspike.scheduler.impl

Source Code of org.apache.deltaspike.scheduler.impl.QuartzScheduler$InjectionAwareJobListener

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.deltaspike.scheduler.impl;

import org.apache.deltaspike.cdise.api.ContextControl;
import org.apache.deltaspike.core.api.config.ConfigResolver;
import org.apache.deltaspike.core.api.provider.BeanProvider;
import org.apache.deltaspike.core.util.ClassUtils;
import org.apache.deltaspike.core.util.ExceptionUtils;
import org.apache.deltaspike.core.util.PropertyFileUtils;
import org.apache.deltaspike.core.util.ProxyUtils;
import org.apache.deltaspike.core.util.metadata.AnnotationInstanceProvider;
import org.apache.deltaspike.scheduler.api.Scheduled;
import org.apache.deltaspike.scheduler.spi.Scheduler;
import org.quartz.CronScheduleBuilder;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.JobListener;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;

//vetoed class (see SchedulerExtension)
public class QuartzScheduler implements Scheduler<Job>
{
    private static final Logger LOG = Logger.getLogger(QuartzScheduler.class.getName());
    private static final Scheduled DEFAULT_SCHEDULED_LITERAL = AnnotationInstanceProvider.of(Scheduled.class);

    protected org.quartz.Scheduler scheduler;

    @Override
    public void start()
    {
        if (this.scheduler != null)
        {
            throw new UnsupportedOperationException("the scheduler is started already");
        }

        SchedulerFactory schedulerFactory = null;
        try
        {
            Properties properties = new Properties();
            properties.put(StdSchedulerFactory.PROP_SCHED_JOB_FACTORY_CLASS, CdiAwareJobFactory.class.getName());

            try
            {
                ResourceBundle config = loadCustomQuartzConfig();

                Enumeration<String> keys = config.getKeys();
                String key;
                while (keys.hasMoreElements())
                {
                    key = keys.nextElement();
                    properties.put(key, config.getString(key));
                }
            }
            catch (Exception e1)
            {
                LOG.info("no custom quartz-config file found. falling back to the default config provided by quartz.");

                InputStream inputStream = null;
                try
                {
                    inputStream = ClassUtils.getClassLoader(null).getResourceAsStream("org/quartz/quartz.properties");
                    properties.load(inputStream);
                }
                catch (Exception e2)
                {
                    LOG.warning("failed to load quartz default-config");
                    schedulerFactory = new StdSchedulerFactory();
                }
                finally
                {
                    if (inputStream != null)
                    {
                        inputStream.close();
                    }
                }
            }
            if (schedulerFactory == null)
            {
                schedulerFactory = new StdSchedulerFactory(properties);
            }
        }
        catch (Exception e)
        {
            LOG.log(Level.WARNING, "fallback to default scheduler-factory", e);
            schedulerFactory = new StdSchedulerFactory();
        }

        try
        {
            this.scheduler = schedulerFactory.getScheduler();
            this.scheduler.getListenerManager().addJobListener(new InjectionAwareJobListener());

            if (!this.scheduler.isStarted())
            {
                String delayedStart =
                    ConfigResolver.getPropertyValue("deltaspike.scheduler.delayed_start_in_seconds", "1");
                this.scheduler.startDelayed(Integer.parseInt(delayedStart));
            }
        }
        catch (SchedulerException e)
        {
            throw ExceptionUtils.throwAsRuntimeException(e);
        }
    }

    protected ResourceBundle loadCustomQuartzConfig()
    {
        String configFile =
            ConfigResolver.getPropertyValue("deltaspike.scheduler.quartz_config-file", "quartz.properties");
        return PropertyFileUtils.getResourceBundle(configFile);
    }

    @Override
    public void stop()
    {
        try
        {
            if (this.scheduler != null && this.scheduler.isStarted())
            {
                String forceStop = ConfigResolver.getPropertyValue("deltaspike.scheduler.force_stop", "true");

                this.scheduler.shutdown(Boolean.parseBoolean(forceStop));
                this.scheduler = null;
            }
        }
        catch (SchedulerException e)
        {
            throw ExceptionUtils.throwAsRuntimeException(e);
        }
    }

    @Override
    public void registerNewJob(Class<? extends Job> jobClass)
    {
        JobKey jobKey = createJobKey(jobClass);

        try
        {
            Scheduled scheduled = jobClass.getAnnotation(Scheduled.class);

            String description = scheduled.description();

            if ("".equals(scheduled.description()))
            {
                description = jobClass.getName();
            }

            JobDetail jobDetail = this.scheduler.getJobDetail(jobKey);
            Trigger trigger;

            if (jobDetail == null)
            {
                jobDetail = JobBuilder.newJob(jobClass)
                        .withDescription(description)
                        .withIdentity(jobKey)
                        .build();

                trigger = TriggerBuilder.newTrigger()
                        .withSchedule(CronScheduleBuilder.cronSchedule(scheduled.cronExpression()))
                        .build();

                this.scheduler.scheduleJob(jobDetail, trigger);
            }
            else if (scheduled.overrideOnStartup())
            {
                List<? extends Trigger> existingTriggers = this.scheduler.getTriggersOfJob(jobKey);

                if (existingTriggers == null || existingTriggers.isEmpty())
                {
                    //TODO re-visit it
                    trigger = TriggerBuilder.newTrigger()
                            .withSchedule(CronScheduleBuilder.cronSchedule(scheduled.cronExpression()))
                            .build();

                    this.scheduler.scheduleJob(jobDetail, trigger);
                    return;
                }

                if (existingTriggers.size() > 1)
                {
                    throw new IllegalStateException("multiple triggers found for " + jobKey + " ('" + jobDetail + "')" +
                        ", but aren't supported by @" + Scheduled.class.getName() + "#overrideOnStartup");
                }

                trigger = existingTriggers.iterator().next();

                trigger = TriggerBuilder.newTrigger()
                        .withIdentity(trigger.getKey())
                        .withSchedule(CronScheduleBuilder.cronSchedule(scheduled.cronExpression()))
                        .build();

                this.scheduler.rescheduleJob(trigger.getKey(), trigger);
            }
            else
            {
                Logger.getLogger(QuartzScheduler.class.getName()).info(jobKey + " exists already and will be ignored.");
            }
        }
        catch (SchedulerException e)
        {
            throw ExceptionUtils.throwAsRuntimeException(e);
        }
    }

    @Override
    public void startJobManually(Class<? extends Job> jobClass)
    {
        try
        {
            this.scheduler.triggerJob(createJobKey(jobClass));
        }
        catch (SchedulerException e)
        {
            throw ExceptionUtils.throwAsRuntimeException(e);
        }
    }

    @Override
    public void interruptJob(Class<? extends Job> jobClass)
    {
        try
        {
            this.scheduler.interrupt(createJobKey(jobClass));
        }
        catch (SchedulerException e)
        {
            throw ExceptionUtils.throwAsRuntimeException(e);
        }
    }

    @Override
    public void pauseJob(Class<? extends Job> jobClass)
    {
        try
        {
            this.scheduler.pauseJob(createJobKey(jobClass));
        }
        catch (SchedulerException e)
        {
            throw ExceptionUtils.throwAsRuntimeException(e);
        }
    }

    @Override
    public void resumeJob(Class<? extends Job> jobClass)
    {
        try
        {
            this.scheduler.resumeJob(createJobKey(jobClass));
        }
        catch (SchedulerException e)
        {
            throw ExceptionUtils.throwAsRuntimeException(e);
        }
    }

    @Override
    public boolean isExecutingJob(Class<? extends Job> jobClass)
    {
        try
        {
            JobKey jobKey = createJobKey(jobClass);
            JobDetail jobDetail = this.scheduler.getJobDetail(jobKey);

            if (jobDetail == null)
            {
                return false;
            }

            for (JobExecutionContext jobExecutionContext : this.scheduler.getCurrentlyExecutingJobs())
            {
                if (jobKey.equals(jobExecutionContext.getJobDetail().getKey()))
                {
                    return true;
                }
            }

            return false;
        }
        catch (SchedulerException e)
        {
            throw ExceptionUtils.throwAsRuntimeException(e);
        }
    }

    private static JobKey createJobKey(Class<?> jobClass)
    {
        Scheduled scheduled = jobClass.getAnnotation(Scheduled.class);

        if (scheduled == null)
        {
            throw new IllegalStateException("@" + Scheduled.class.getName() + " is missing on " + jobClass.getName());
        }

        String groupName = scheduled.group().getSimpleName();
        String jobName = jobClass.getSimpleName();

        if (!Scheduled.class.getSimpleName().equals(groupName))
        {
            return new JobKey(jobName, groupName);
        }
        return new JobKey(jobName);
    }

    private class InjectionAwareJobListener implements JobListener
    {
        private Stack<Class<? extends Annotation>> scopes = new Stack<Class<? extends Annotation>>();
        private ContextControl contextControl;

        @Override
        public String getName()
        {
            return getClass().getName();
        }

        @Override
        public void jobToBeExecuted(JobExecutionContext jobExecutionContext)
        {
            Class<?> jobClass = ProxyUtils.getUnproxiedClass(jobExecutionContext.getJobInstance().getClass());
            Scheduled scheduled = jobClass.getAnnotation(Scheduled.class);

            //can happen with manually registered job-instances (via #unwrap)
            if (scheduled == null)
            {
                scheduled = DEFAULT_SCHEDULED_LITERAL;
            }

            Collections.addAll(this.scopes, scheduled.startScopes());

            if (!this.scopes.isEmpty())
            {
                this.contextControl = BeanProvider.getContextualReference(ContextControl.class);

                for (Class<? extends Annotation> scopeAnnotation : this.scopes)
                {
                    contextControl.startContext(scopeAnnotation);
                }
            }

            boolean jobInstanceIsBean;

            try
            {
                jobInstanceIsBean =
                    Boolean.TRUE.equals(jobExecutionContext.getScheduler().getContext().get(jobClass.getName()));
            }
            catch (SchedulerException e)
            {
                jobInstanceIsBean = false;
            }

            if (!jobInstanceIsBean)
            {
                BeanProvider.injectFields(jobExecutionContext.getJobInstance());
            }
        }

        @Override
        public void jobExecutionVetoed(JobExecutionContext context)
        {
            stopStartedScopes();
        }

        @Override
        public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException)
        {
            stopStartedScopes();
        }

        private void stopStartedScopes()
        {
            while (!this.scopes.empty())
            {
                this.contextControl.stopContext(this.scopes.pop());
            }
        }
    }

    @Override
    public <S> S unwrap(Class<? extends S> schedulerClass)
    {
        if (schedulerClass.isAssignableFrom(this.scheduler.getClass()))
        {
            return (S)this.scheduler;
        }

        throw new IllegalArgumentException(schedulerClass.getName() +
            " isn't compatible with " + this.scheduler.getClass().getName());
    }
}
TOP

Related Classes of org.apache.deltaspike.scheduler.impl.QuartzScheduler$InjectionAwareJobListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.