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 org.quartz.SchedulerContext; 020import org.quartz.spi.TriggerFiredBundle; 021 022import org.springframework.beans.BeanWrapper; 023import org.springframework.beans.MutablePropertyValues; 024import org.springframework.beans.PropertyAccessorFactory; 025import org.springframework.beans.factory.config.AutowireCapableBeanFactory; 026import org.springframework.context.ApplicationContext; 027import org.springframework.context.ApplicationContextAware; 028import org.springframework.lang.Nullable; 029 030/** 031 * Subclass of {@link AdaptableJobFactory} that also supports Spring-style 032 * dependency injection on bean properties. This is essentially the direct 033 * equivalent of Spring's {@link QuartzJobBean} in the shape of a Quartz 034 * {@link org.quartz.spi.JobFactory}. 035 * 036 * <p>Applies scheduler context, job data map and trigger data map entries 037 * as bean property values. If no matching bean property is found, the entry 038 * is by default simply ignored. This is analogous to QuartzJobBean's behavior. 039 * 040 * <p>Compatible with Quartz 2.1.4 and higher, as of Spring 4.1. 041 * 042 * @author Juergen Hoeller 043 * @since 2.0 044 * @see SchedulerFactoryBean#setJobFactory 045 * @see QuartzJobBean 046 */ 047public class SpringBeanJobFactory extends AdaptableJobFactory 048 implements ApplicationContextAware, SchedulerContextAware { 049 050 @Nullable 051 private String[] ignoredUnknownProperties; 052 053 @Nullable 054 private ApplicationContext applicationContext; 055 056 @Nullable 057 private SchedulerContext schedulerContext; 058 059 060 /** 061 * Specify the unknown properties (not found in the bean) that should be ignored. 062 * <p>Default is {@code null}, indicating that all unknown properties 063 * should be ignored. Specify an empty array to throw an exception in case 064 * of any unknown properties, or a list of property names that should be 065 * ignored if there is no corresponding property found on the particular 066 * job class (all other unknown properties will still trigger an exception). 067 */ 068 public void setIgnoredUnknownProperties(String... ignoredUnknownProperties) { 069 this.ignoredUnknownProperties = ignoredUnknownProperties; 070 } 071 072 @Override 073 public void setApplicationContext(ApplicationContext applicationContext) { 074 this.applicationContext = applicationContext; 075 } 076 077 @Override 078 public void setSchedulerContext(SchedulerContext schedulerContext) { 079 this.schedulerContext = schedulerContext; 080 } 081 082 083 /** 084 * Create the job instance, populating it with property values taken 085 * from the scheduler context, job data map and trigger data map. 086 */ 087 @Override 088 protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { 089 Object job = (this.applicationContext != null ? 090 this.applicationContext.getAutowireCapableBeanFactory().createBean( 091 bundle.getJobDetail().getJobClass(), AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false) : 092 super.createJobInstance(bundle)); 093 094 if (isEligibleForPropertyPopulation(job)) { 095 BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(job); 096 MutablePropertyValues pvs = new MutablePropertyValues(); 097 if (this.schedulerContext != null) { 098 pvs.addPropertyValues(this.schedulerContext); 099 } 100 pvs.addPropertyValues(bundle.getJobDetail().getJobDataMap()); 101 pvs.addPropertyValues(bundle.getTrigger().getJobDataMap()); 102 if (this.ignoredUnknownProperties != null) { 103 for (String propName : this.ignoredUnknownProperties) { 104 if (pvs.contains(propName) && !bw.isWritableProperty(propName)) { 105 pvs.removePropertyValue(propName); 106 } 107 } 108 bw.setPropertyValues(pvs); 109 } 110 else { 111 bw.setPropertyValues(pvs, true); 112 } 113 } 114 115 return job; 116 } 117 118 /** 119 * Return whether the given job object is eligible for having 120 * its bean properties populated. 121 * <p>The default implementation ignores {@link QuartzJobBean} instances, 122 * which will inject bean properties themselves. 123 * @param jobObject the job object to introspect 124 * @see QuartzJobBean 125 */ 126 protected boolean isEligibleForPropertyPopulation(Object jobObject) { 127 return (!(jobObject instanceof QuartzJobBean)); 128 } 129 130}