001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.scheduling.quartz; 018 019import java.util.Map; 020 021import org.quartz.Job; 022import org.quartz.JobDataMap; 023import org.quartz.JobDetail; 024import org.quartz.Scheduler; 025import org.quartz.impl.JobDetailImpl; 026 027import org.springframework.beans.factory.BeanNameAware; 028import org.springframework.beans.factory.FactoryBean; 029import org.springframework.beans.factory.InitializingBean; 030import org.springframework.context.ApplicationContext; 031import org.springframework.context.ApplicationContextAware; 032import org.springframework.lang.Nullable; 033import org.springframework.util.Assert; 034 035/** 036 * A Spring {@link FactoryBean} for creating a Quartz {@link org.quartz.JobDetail} 037 * instance, supporting bean-style usage for JobDetail configuration. 038 * 039 * <p>{@code JobDetail(Impl)} itself is already a JavaBean but lacks 040 * sensible defaults. This class uses the Spring bean name as job name, 041 * and the Quartz default group ("DEFAULT") as job group if not specified. 042 * 043 * @author Juergen Hoeller 044 * @since 3.1 045 * @see #setName 046 * @see #setGroup 047 * @see org.springframework.beans.factory.BeanNameAware 048 * @see org.quartz.Scheduler#DEFAULT_GROUP 049 */ 050public class JobDetailFactoryBean 051 implements FactoryBean<JobDetail>, BeanNameAware, ApplicationContextAware, InitializingBean { 052 053 @Nullable 054 private String name; 055 056 @Nullable 057 private String group; 058 059 @Nullable 060 private Class<? extends Job> jobClass; 061 062 private JobDataMap jobDataMap = new JobDataMap(); 063 064 private boolean durability = false; 065 066 private boolean requestsRecovery = false; 067 068 @Nullable 069 private String description; 070 071 @Nullable 072 private String beanName; 073 074 @Nullable 075 private ApplicationContext applicationContext; 076 077 @Nullable 078 private String applicationContextJobDataKey; 079 080 @Nullable 081 private JobDetail jobDetail; 082 083 084 /** 085 * Specify the job's name. 086 */ 087 public void setName(String name) { 088 this.name = name; 089 } 090 091 /** 092 * Specify the job's group. 093 */ 094 public void setGroup(String group) { 095 this.group = group; 096 } 097 098 /** 099 * Specify the job's implementation class. 100 */ 101 public void setJobClass(Class<? extends Job> jobClass) { 102 this.jobClass = jobClass; 103 } 104 105 /** 106 * Set the job's JobDataMap. 107 * @see #setJobDataAsMap 108 */ 109 public void setJobDataMap(JobDataMap jobDataMap) { 110 this.jobDataMap = jobDataMap; 111 } 112 113 /** 114 * Return the job's JobDataMap. 115 */ 116 public JobDataMap getJobDataMap() { 117 return this.jobDataMap; 118 } 119 120 /** 121 * Register objects in the JobDataMap via a given Map. 122 * <p>These objects will be available to this Job only, 123 * in contrast to objects in the SchedulerContext. 124 * <p>Note: When using persistent Jobs whose JobDetail will be kept in the 125 * database, do not put Spring-managed beans or an ApplicationContext 126 * reference into the JobDataMap but rather into the SchedulerContext. 127 * @param jobDataAsMap a Map with String keys and any objects as values 128 * (for example Spring-managed beans) 129 * @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setSchedulerContextAsMap 130 */ 131 public void setJobDataAsMap(Map<String, ?> jobDataAsMap) { 132 getJobDataMap().putAll(jobDataAsMap); 133 } 134 135 /** 136 * Specify the job's durability, i.e. whether it should remain stored 137 * in the job store even if no triggers point to it anymore. 138 */ 139 public void setDurability(boolean durability) { 140 this.durability = durability; 141 } 142 143 /** 144 * Set the recovery flag for this job, i.e. whether or not the job should 145 * get re-executed if a 'recovery' or 'fail-over' situation is encountered. 146 */ 147 public void setRequestsRecovery(boolean requestsRecovery) { 148 this.requestsRecovery = requestsRecovery; 149 } 150 151 /** 152 * Set a textual description for this job. 153 */ 154 public void setDescription(String description) { 155 this.description = description; 156 } 157 158 @Override 159 public void setBeanName(String beanName) { 160 this.beanName = beanName; 161 } 162 163 @Override 164 public void setApplicationContext(ApplicationContext applicationContext) { 165 this.applicationContext = applicationContext; 166 } 167 168 /** 169 * Set the key of an ApplicationContext reference to expose in the JobDataMap, 170 * for example "applicationContext". Default is none. 171 * Only applicable when running in a Spring ApplicationContext. 172 * <p>In case of a QuartzJobBean, the reference will be applied to the Job 173 * instance as bean property. An "applicationContext" attribute will correspond 174 * to a "setApplicationContext" method in that scenario. 175 * <p>Note that BeanFactory callback interfaces like ApplicationContextAware 176 * are not automatically applied to Quartz Job instances, because Quartz 177 * itself is responsible for the lifecycle of its Jobs. 178 * <p><b>Note: When using persistent job stores where JobDetail contents will 179 * be kept in the database, do not put an ApplicationContext reference into 180 * the JobDataMap but rather into the SchedulerContext.</b> 181 * @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setApplicationContextSchedulerContextKey 182 * @see org.springframework.context.ApplicationContext 183 */ 184 public void setApplicationContextJobDataKey(String applicationContextJobDataKey) { 185 this.applicationContextJobDataKey = applicationContextJobDataKey; 186 } 187 188 189 @Override 190 public void afterPropertiesSet() { 191 Assert.notNull(this.jobClass, "Property 'jobClass' is required"); 192 193 if (this.name == null) { 194 this.name = this.beanName; 195 } 196 if (this.group == null) { 197 this.group = Scheduler.DEFAULT_GROUP; 198 } 199 if (this.applicationContextJobDataKey != null) { 200 if (this.applicationContext == null) { 201 throw new IllegalStateException( 202 "JobDetailBean needs to be set up in an ApplicationContext " + 203 "to be able to handle an 'applicationContextJobDataKey'"); 204 } 205 getJobDataMap().put(this.applicationContextJobDataKey, this.applicationContext); 206 } 207 208 JobDetailImpl jdi = new JobDetailImpl(); 209 jdi.setName(this.name != null ? this.name : toString()); 210 jdi.setGroup(this.group); 211 jdi.setJobClass(this.jobClass); 212 jdi.setJobDataMap(this.jobDataMap); 213 jdi.setDurability(this.durability); 214 jdi.setRequestsRecovery(this.requestsRecovery); 215 jdi.setDescription(this.description); 216 this.jobDetail = jdi; 217 } 218 219 220 @Override 221 @Nullable 222 public JobDetail getObject() { 223 return this.jobDetail; 224 } 225 226 @Override 227 public Class<?> getObjectType() { 228 return JobDetail.class; 229 } 230 231 @Override 232 public boolean isSingleton() { 233 return true; 234 } 235 236}