Repository: deltaspike Updated Branches: refs/heads/master e79b665a4 -> aa1893f71
DELTASPIKE-1054 optional mode for switching from org.quartz.Job to java.lang.Runnable Project: http://git-wip-us.apache.org/repos/asf/deltaspike/repo Commit: http://git-wip-us.apache.org/repos/asf/deltaspike/commit/aa1893f7 Tree: http://git-wip-us.apache.org/repos/asf/deltaspike/tree/aa1893f7 Diff: http://git-wip-us.apache.org/repos/asf/deltaspike/diff/aa1893f7 Branch: refs/heads/master Commit: aa1893f71d1658b4f052600453d89e83728b5233 Parents: e79b665 Author: gpetracek <[email protected]> Authored: Wed Dec 30 16:50:53 2015 +0100 Committer: gpetracek <[email protected]> Committed: Wed Dec 30 18:34:41 2015 +0100 ---------------------------------------------------------------------- .../scheduler/impl/AbstractQuartzScheduler.java | 474 +++++++++++++++++++ .../scheduler/impl/JobQuartzScheduler.java | 31 ++ .../scheduler/impl/JobRunnableAdapter.java | 41 ++ .../scheduler/impl/QuartzScheduler.java | 466 ------------------ .../scheduler/impl/RunnableQuartzScheduler.java | 37 ++ .../scheduler/impl/SchedulerExtension.java | 14 + ...rg.apache.deltaspike.scheduler.spi.Scheduler | 10 +- .../scheduler/custom/QuartzDeactivator.java | 3 +- 8 files changed, 608 insertions(+), 468 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/deltaspike/blob/aa1893f7/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/AbstractQuartzScheduler.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/AbstractQuartzScheduler.java b/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/AbstractQuartzScheduler.java new file mode 100644 index 0000000..bd85f93 --- /dev/null +++ b/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/AbstractQuartzScheduler.java @@ -0,0 +1,474 @@ +/* + * 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.provider.BeanProvider; +import org.apache.deltaspike.core.api.provider.DependentProvider; +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; + +public abstract class AbstractQuartzScheduler<T> implements Scheduler<T> +{ + private static final Logger LOG = Logger.getLogger(AbstractQuartzScheduler.class.getName()); + private static final Scheduled DEFAULT_SCHEDULED_LITERAL = AnnotationInstanceProvider.of(Scheduled.class); + + private static ThreadLocal<JobListenerContext> currentJobListenerContext = new ThreadLocal<JobListenerContext>(); + + 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(); + if (SchedulerBaseConfig.LifecycleIntegration.START_SCOPES_PER_JOB) + { + this.scheduler.getListenerManager().addJobListener(new InjectionAwareJobListener()); + } + if (!this.scheduler.isStarted()) + { + this.scheduler.startDelayed(SchedulerBaseConfig.LifecycleIntegration.DELAYED_START_IN_SECONDS); + } + } + catch (SchedulerException e) + { + throw ExceptionUtils.throwAsRuntimeException(e); + } + } + + protected ResourceBundle loadCustomQuartzConfig() + { + //don't use quartz.properties as default-value + String configFile = SchedulerBaseConfig.SCHEDULER_CONFIG_FILE; + return PropertyFileUtils.getResourceBundle(configFile); + } + + @Override + public void stop() + { + try + { + if (this.scheduler != null && this.scheduler.isStarted()) + { + this.scheduler.shutdown(SchedulerBaseConfig.LifecycleIntegration.FORCE_STOP); + this.scheduler = null; + } + } + catch (SchedulerException e) + { + throw ExceptionUtils.throwAsRuntimeException(e); + } + } + + @Override + public void registerNewJob(Class<? extends T> 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) + { + Class<? extends Job> jobClassToAdd = createFinalJobClass(jobClass); + jobDetail = JobBuilder.newJob(jobClassToAdd) + .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(AbstractQuartzScheduler.class.getName()).info( + jobKey + " exists already and will be ignored."); + } + } + catch (SchedulerException e) + { + throw ExceptionUtils.throwAsRuntimeException(e); + } + } + + protected abstract Class<? extends Job> createFinalJobClass(Class<? extends T> jobClass); + + @Override + public void startJobManually(Class<? extends T> jobClass) + { + try + { + this.scheduler.triggerJob(createJobKey(jobClass)); + } + catch (SchedulerException e) + { + throw ExceptionUtils.throwAsRuntimeException(e); + } + } + + @Override + public void interruptJob(Class<? extends T> jobClass) + { + try + { + this.scheduler.interrupt(createJobKey(jobClass)); + } + catch (SchedulerException e) + { + throw ExceptionUtils.throwAsRuntimeException(e); + } + } + + @Override + public boolean deleteJob(Class<? extends T> jobClass) + { + try + { + return this.scheduler.deleteJob(createJobKey(jobClass)); + } + catch (SchedulerException e) + { + throw ExceptionUtils.throwAsRuntimeException(e); + } + } + + @Override + public void pauseJob(Class<? extends T> jobClass) + { + try + { + this.scheduler.pauseJob(createJobKey(jobClass)); + } + catch (SchedulerException e) + { + throw ExceptionUtils.throwAsRuntimeException(e); + } + } + + @Override + public void resumeJob(Class<? extends T> jobClass) + { + try + { + this.scheduler.resumeJob(createJobKey(jobClass)); + } + catch (SchedulerException e) + { + throw ExceptionUtils.throwAsRuntimeException(e); + } + } + + @Override + public boolean isExecutingJob(Class<? extends T> 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 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 = getJobName(jobClass); + + if (!Scheduled.class.getSimpleName().equals(groupName)) + { + return new JobKey(jobName, groupName); + } + return new JobKey(jobName); + } + + protected String getJobName(Class<?> jobClass) + { + return jobClass.getSimpleName(); + } + + private class InjectionAwareJobListener implements JobListener + { + @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; + } + + JobListenerContext jobListenerContext = new JobListenerContext(); + currentJobListenerContext.set(jobListenerContext); + jobListenerContext.startContexts(scheduled); + + 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() + { + JobListenerContext jobListenerContext = currentJobListenerContext.get(); + if (jobListenerContext != null) + { + jobListenerContext.stopStartedScopes(); + currentJobListenerContext.set(null); + currentJobListenerContext.remove(); + } + } + } + + private static class JobListenerContext + { + private Stack<Class<? extends Annotation>> scopes = new Stack<Class<? extends Annotation>>(); + private DependentProvider<ContextControl> contextControl; + + public void startContexts(Scheduled scheduled) + { + Collections.addAll(this.scopes, scheduled.startScopes()); + + if (!this.scopes.isEmpty()) + { + this.contextControl = BeanProvider.getDependent(ContextControl.class); + + for (Class<? extends Annotation> scopeAnnotation : this.scopes) + { + contextControl.get().startContext(scopeAnnotation); + } + } + } + + private void stopStartedScopes() + { + if (this.contextControl == null) + { + return; + } + + while (!this.scopes.empty()) + { + this.contextControl.get().stopContext(this.scopes.pop()); + } + this.contextControl.destroy(); + } + } + + @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()); + } +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/aa1893f7/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/JobQuartzScheduler.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/JobQuartzScheduler.java b/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/JobQuartzScheduler.java new file mode 100644 index 0000000..785026e --- /dev/null +++ b/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/JobQuartzScheduler.java @@ -0,0 +1,31 @@ +/* + * 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.quartz.Job; + +//vetoed class (see SchedulerExtension) +public class JobQuartzScheduler extends AbstractQuartzScheduler<Job> +{ + @Override + protected Class<? extends Job> createFinalJobClass(Class<? extends Job> jobClass) + { + return jobClass; + } +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/aa1893f7/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/JobRunnableAdapter.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/JobRunnableAdapter.java b/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/JobRunnableAdapter.java new file mode 100644 index 0000000..ab03f27 --- /dev/null +++ b/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/JobRunnableAdapter.java @@ -0,0 +1,41 @@ +/* + * 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.core.api.provider.BeanProvider; +import org.apache.deltaspike.core.util.ClassUtils; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +import javax.enterprise.inject.Typed; + +@Typed() +public class JobRunnableAdapter implements Job +{ + @Override + public void execute(JobExecutionContext context) throws JobExecutionException + { + Class<? extends Runnable> jobClass = + ClassUtils.tryToLoadClassForName(context.getJobDetail().getKey().getName(), Runnable.class); + + Runnable runnableBean = BeanProvider.getContextualReference(jobClass); + runnableBean.run(); + } +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/aa1893f7/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/QuartzScheduler.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/QuartzScheduler.java b/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/QuartzScheduler.java deleted file mode 100644 index 44e6417..0000000 --- a/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/QuartzScheduler.java +++ /dev/null @@ -1,466 +0,0 @@ -/* - * 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.provider.BeanProvider; -import org.apache.deltaspike.core.api.provider.DependentProvider; -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); - - private static ThreadLocal<JobListenerContext> currentJobListenerContext = new ThreadLocal<JobListenerContext>(); - - 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(); - if (SchedulerBaseConfig.LifecycleIntegration.START_SCOPES_PER_JOB) - { - this.scheduler.getListenerManager().addJobListener(new InjectionAwareJobListener()); - } - if (!this.scheduler.isStarted()) - { - this.scheduler.startDelayed(SchedulerBaseConfig.LifecycleIntegration.DELAYED_START_IN_SECONDS); - } - } - catch (SchedulerException e) - { - throw ExceptionUtils.throwAsRuntimeException(e); - } - } - - protected ResourceBundle loadCustomQuartzConfig() - { - //don't use quartz.properties as default-value - String configFile = SchedulerBaseConfig.SCHEDULER_CONFIG_FILE; - return PropertyFileUtils.getResourceBundle(configFile); - } - - @Override - public void stop() - { - try - { - if (this.scheduler != null && this.scheduler.isStarted()) - { - this.scheduler.shutdown(SchedulerBaseConfig.LifecycleIntegration.FORCE_STOP); - 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 boolean deleteJob(Class<? extends Job> jobClass) - { - try - { - return this.scheduler.deleteJob(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 - { - @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; - } - - JobListenerContext jobListenerContext = new JobListenerContext(); - currentJobListenerContext.set(jobListenerContext); - jobListenerContext.startContexts(scheduled); - - 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() - { - JobListenerContext jobListenerContext = currentJobListenerContext.get(); - if (jobListenerContext != null) - { - jobListenerContext.stopStartedScopes(); - currentJobListenerContext.set(null); - currentJobListenerContext.remove(); - } - } - } - - private class JobListenerContext - { - private Stack<Class<? extends Annotation>> scopes = new Stack<Class<? extends Annotation>>(); - private DependentProvider<ContextControl> contextControl; - - public void startContexts(Scheduled scheduled) - { - Collections.addAll(this.scopes, scheduled.startScopes()); - - if (!this.scopes.isEmpty()) - { - this.contextControl = BeanProvider.getDependent(ContextControl.class); - - for (Class<? extends Annotation> scopeAnnotation : this.scopes) - { - contextControl.get().startContext(scopeAnnotation); - } - } - } - - private void stopStartedScopes() - { - if (this.contextControl == null) - { - return; - } - - while (!this.scopes.empty()) - { - this.contextControl.get().stopContext(this.scopes.pop()); - } - this.contextControl.destroy(); - } - } - - @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()); - } -} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/aa1893f7/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/RunnableQuartzScheduler.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/RunnableQuartzScheduler.java b/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/RunnableQuartzScheduler.java new file mode 100644 index 0000000..9d11f29 --- /dev/null +++ b/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/RunnableQuartzScheduler.java @@ -0,0 +1,37 @@ +/* + * 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.quartz.Job; + +//vetoed class (see SchedulerExtension) +public class RunnableQuartzScheduler extends AbstractQuartzScheduler<Runnable> +{ + @Override + protected String getJobName(Class<?> jobClass) + { + return jobClass.getName(); + } + + @Override + protected Class<? extends Job> createFinalJobClass(Class<? extends Runnable> jobClass) + { + return JobRunnableAdapter.class; + } +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/aa1893f7/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/SchedulerExtension.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/SchedulerExtension.java b/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/SchedulerExtension.java index ea113bc..907d2c0 100644 --- a/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/SchedulerExtension.java +++ b/deltaspike/modules/scheduler/impl/src/main/java/org/apache/deltaspike/scheduler/impl/SchedulerExtension.java @@ -170,6 +170,7 @@ public class SchedulerExtension implements Extension, Deactivatable { for (Scheduler scheduler : availableSchedulers) { + //in case of implementing the Scheduler interface directly for (Type interfaceClass : scheduler.getClass().getGenericInterfaces()) { if (!(interfaceClass instanceof ParameterizedType) || @@ -183,6 +184,19 @@ public class SchedulerExtension implements Extension, Deactivatable return scheduler; } } + + //in case of extending e.g. AbstractQuartzScheduler + if (scheduler.getClass().getGenericSuperclass() instanceof ParameterizedType) + { + ParameterizedType parameterizedType = (ParameterizedType) scheduler.getClass().getGenericSuperclass(); + for (Type typeArgument : parameterizedType.getActualTypeArguments()) + { + if (jobClass.isAssignableFrom((Class)typeArgument)) + { + return scheduler; + } + } + } } return null; } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/aa1893f7/deltaspike/modules/scheduler/impl/src/main/resources/META-INF/services/org.apache.deltaspike.scheduler.spi.Scheduler ---------------------------------------------------------------------- diff --git a/deltaspike/modules/scheduler/impl/src/main/resources/META-INF/services/org.apache.deltaspike.scheduler.spi.Scheduler b/deltaspike/modules/scheduler/impl/src/main/resources/META-INF/services/org.apache.deltaspike.scheduler.spi.Scheduler index f8883a7..3e03601 100644 --- a/deltaspike/modules/scheduler/impl/src/main/resources/META-INF/services/org.apache.deltaspike.scheduler.spi.Scheduler +++ b/deltaspike/modules/scheduler/impl/src/main/resources/META-INF/services/org.apache.deltaspike.scheduler.spi.Scheduler @@ -93,4 +93,12 @@ # under the License. ##################################################################################### -org.apache.deltaspike.scheduler.impl.QuartzScheduler \ No newline at end of file +#only one of the following schedulers will get active + +#default scheduler: +org.apache.deltaspike.scheduler.impl.JobQuartzScheduler + +#alternative scheduler - add +# deltaspike.scheduler.job-class=java.lang.Runnable +#to META-INF/apache-deltaspike.properties +org.apache.deltaspike.scheduler.impl.RunnableQuartzScheduler \ No newline at end of file http://git-wip-us.apache.org/repos/asf/deltaspike/blob/aa1893f7/deltaspike/modules/scheduler/impl/src/test/java/org/apache/deltaspike/test/scheduler/custom/QuartzDeactivator.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/scheduler/impl/src/test/java/org/apache/deltaspike/test/scheduler/custom/QuartzDeactivator.java b/deltaspike/modules/scheduler/impl/src/test/java/org/apache/deltaspike/test/scheduler/custom/QuartzDeactivator.java index 0a929bf..f370789 100644 --- a/deltaspike/modules/scheduler/impl/src/test/java/org/apache/deltaspike/test/scheduler/custom/QuartzDeactivator.java +++ b/deltaspike/modules/scheduler/impl/src/test/java/org/apache/deltaspike/test/scheduler/custom/QuartzDeactivator.java @@ -28,6 +28,7 @@ public class QuartzDeactivator implements ClassDeactivator @Override public Boolean isActivated(Class<? extends Deactivatable> targetClass) { - return !"QuartzScheduler".equals(targetClass.getSimpleName()); + return !"QuartzScheduler".equals(targetClass.getSimpleName()) && + !"RunnableQuartzScheduler".equals(targetClass.getSimpleName()); } }
