diff --git a/build.gradle b/build.gradle index 703d8cbf34e..d63395d49da 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,6 @@ configure(allprojects) { project -> ext.hibernate4Version = "4.2.11.Final" ext.hibValVersion = "4.3.1.Final" ext.hsqldbVersion = "2.3.2" - ext.jackson1Version = "1.9.13" ext.jackson2Version = "2.3.1" ext.jasperReportsVersion = "5.5.1" ext.jettyVersion = "9.1.3.v20140225" @@ -495,7 +494,6 @@ project("spring-jms") { optional("aopalliance:aopalliance:1.0") optional("javax.transaction:javax.transaction-api:1.2") optional("javax.resource:connector-api:1.5") - optional("org.codehaus.jackson:jackson-mapper-asl:${jackson1Version}") optional("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}") } } @@ -530,9 +528,7 @@ project("spring-context-support") { optional("javax.cache:cache-api:1.0.0-RC1") optional("com.google.guava:guava:16.0.1") optional("net.sf.ehcache:ehcache-core:2.6.5") - optional("org.quartz-scheduler:quartz:1.8.6") { - exclude group: "org.slf4j", module: "slf4j-log4j12" - } + optional("org.quartz-scheduler:quartz:2.1.7") optional("org.codehaus.fabric3.api:commonj:1.1.0") optional("org.apache.velocity:velocity:1.7") optional("org.freemarker:freemarker:2.3.20") @@ -568,7 +564,6 @@ project("spring-web") { optional("commons-fileupload:commons-fileupload:1.3.1") optional("org.apache.httpcomponents:httpclient:4.3.3") optional("org.apache.httpcomponents:httpasyncclient:4.0.1") - optional("org.codehaus.jackson:jackson-mapper-asl:${jackson1Version}") optional("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}") optional("rome:rome:1.0") optional("taglibs:standard:1.1.2") @@ -709,7 +704,6 @@ project("spring-webmvc") { optional("net.sf.jasperreports:jasperreports:$jasperReportsVersion") { exclude group: "xml-apis", module: "xml-apis" } - optional("org.codehaus.jackson:jackson-mapper-asl:${jackson1Version}") optional("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}") optional("rome:rome:1.0") optional("org.apache.tiles:tiles-api:${tiles2Version}") @@ -847,7 +841,6 @@ project("spring-test") { testCompile("org.hibernate:hibernate-entitymanager:${hibernate3Version}") testCompile("org.hibernate:hibernate-validator:${hibValVersion}") testCompile("com.thoughtworks.xstream:xstream:${xstreamVersion}") - testCompile("org.codehaus.jackson:jackson-mapper-asl:${jackson1Version}") testCompile("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}") testCompile("rome:rome:1.0") testCompile("org.apache.tiles:tiles-api:${tiles3Version}") diff --git a/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheFactoryBean.java b/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheFactoryBean.java index 1aed482cfbf..0a48df385d8 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheFactoryBean.java +++ b/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,9 +52,8 @@ import org.springframework.util.ClassUtils; *

Note: If the named Cache instance is found, the properties will be ignored and the * Cache instance will be retrieved from the CacheManager. * - *

Note: As of Spring 4.0, Spring's EhCache support requires EhCache 2.1 or higher. - * We recommend the use of EhCache 2.5 or higher. - + *

Note: As of Spring 4.1, Spring's EhCache support requires EhCache 2.5 or higher. + * * @author Juergen Hoeller * @author Dmitriy Kopylenko * @since 1.1.1 @@ -92,10 +91,8 @@ public class EhCacheFactoryBean extends CacheConfiguration implements FactoryBea private Ehcache cache; - @SuppressWarnings("deprecation") public EhCacheFactoryBean() { - // Using deprecated setMaxElementsInMemory method for EhCache 2.1-2.4 compatibility - setMaxElementsInMemory(10000); + setMaxEntriesLocalHeap(10000); setMaxElementsOnDisk(10000000); setTimeToLiveSeconds(120); setTimeToIdleSeconds(120); diff --git a/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java b/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java index a17adb14263..dcc038d6cb4 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java +++ b/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,8 +44,7 @@ import org.springframework.core.io.Resource; * and cares for proper shutdown of the CacheManager. EhCacheManagerFactoryBean is * also necessary for loading EhCache configuration from a non-default config location. * - *

Note: As of Spring 4.0, Spring's EhCache support requires EhCache 2.1 or higher. - * We recommend the use of EhCache 2.5 or higher. + *

Note: As of Spring 4.1, Spring's EhCache support requires EhCache 2.5 or higher. * * @author Juergen Hoeller * @author Dmitriy Kopylenko diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/AdaptableJobFactory.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/AdaptableJobFactory.java index 91dae748775..7aeb07d70ad 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/AdaptableJobFactory.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/AdaptableJobFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,22 +16,17 @@ package org.springframework.scheduling.quartz; -import java.lang.reflect.Method; - import org.quartz.Job; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.spi.JobFactory; import org.quartz.spi.TriggerFiredBundle; -import org.springframework.util.ReflectionUtils; - /** * JobFactory implementation that supports {@link java.lang.Runnable} * objects as well as standard Quartz {@link org.quartz.Job} instances. * - *

Compatible with Quartz 1.8 as well as Quartz 2.0-2.2, as of Spring 4.0. - * Note: Quartz 1.x support is deprecated - please upgrade to Quartz 2.0+. + *

Compatible with Quartz 2.1.4 and higher, as of Spring 4.1. * * @author Juergen Hoeller * @since 2.0 @@ -40,19 +35,8 @@ import org.springframework.util.ReflectionUtils; */ public class AdaptableJobFactory implements JobFactory { - /** - * Quartz 2.0 version of newJob: simply delegates to old newJob variant. - * @see #newJob(org.quartz.spi.TriggerFiredBundle) - */ - public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException { - return newJob(bundle); - } - - /** - * Quartz 1.x version of newJob: contains actual implementation code. - */ @Override - public Job newJob(TriggerFiredBundle bundle) throws SchedulerException { + public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException { try { Object jobObject = createJobInstance(bundle); return adaptJob(jobObject); @@ -71,12 +55,7 @@ public class AdaptableJobFactory implements JobFactory { * @throws Exception if job instantiation failed */ protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { - // Reflectively adapting to differences between Quartz 1.x and Quartz 2.0... - Method getJobDetail = bundle.getClass().getMethod("getJobDetail"); - Object jobDetail = ReflectionUtils.invokeMethod(getJobDetail, bundle); - Method getJobClass = jobDetail.getClass().getMethod("getJobClass"); - Class jobClass = (Class) ReflectionUtils.invokeMethod(getJobClass, jobDetail); - return jobClass.newInstance(); + return bundle.getJobDetail().getJobClass().newInstance(); } /** diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/CronTriggerBean.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/CronTriggerBean.java deleted file mode 100644 index 5a29248d56e..00000000000 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/CronTriggerBean.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * Licensed 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.springframework.scheduling.quartz; - -import java.util.Date; -import java.util.Map; -import java.util.TimeZone; - -import org.quartz.CronTrigger; -import org.quartz.JobDetail; -import org.quartz.Scheduler; - -import org.springframework.beans.factory.BeanNameAware; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.core.Constants; -import org.springframework.util.Assert; - -/** - * Convenience subclass of Quartz's {@link org.quartz.CronTrigger} class, - * making bean-style usage easier. - * - *

{@code CronTrigger} itself is already a JavaBean but lacks sensible defaults. - * This class uses the Spring bean name as job name, the Quartz default group - * ("DEFAULT") as job group, the current time as start time, and indefinite - * repetition, if not specified. - * - *

This class will also register the trigger with the job name and group of - * a given {@link org.quartz.JobDetail}. This allows {@link SchedulerFactoryBean} - * to automatically register a trigger for the corresponding JobDetail, - * instead of registering the JobDetail separately. - * - *

NOTE: This convenience subclass does not work against Quartz 2.0. - * Use Quartz 2.0's native {@code JobDetailImpl} class or the new Quartz 2.0 - * builder API instead. Alternatively, switch to Spring's {@link CronTriggerFactoryBean} - * which largely is a drop-in replacement for this class and its properties and - * consistently works against Quartz 1.x as well as Quartz 2.x. - * - * @author Juergen Hoeller - * @since 18.02.2004 - * @see #setName - * @see #setGroup - * @see #setStartTime - * @see #setJobName - * @see #setJobGroup - * @see #setJobDetail - * @see SchedulerFactoryBean#setTriggers - * @see SchedulerFactoryBean#setJobDetails - * @see SimpleTriggerBean - */ -@SuppressWarnings("serial") -public class CronTriggerBean extends CronTrigger - implements JobDetailAwareTrigger, BeanNameAware, InitializingBean { - - /** Constants for the CronTrigger class */ - private static final Constants constants = new Constants(CronTrigger.class); - - - private JobDetail jobDetail; - - private String beanName; - - private long startDelay = 0; - - - /** - * Register objects in the JobDataMap via a given Map. - *

These objects will be available to this Trigger only, - * in contrast to objects in the JobDetail's data map. - * @param jobDataAsMap Map with String keys and any objects as values - * (for example Spring-managed beans) - * @see JobDetailBean#setJobDataAsMap - */ - public void setJobDataAsMap(Map jobDataAsMap) { - getJobDataMap().putAll(jobDataAsMap); - } - - /** - * Set the misfire instruction via the name of the corresponding - * constant in the {@link org.quartz.CronTrigger} class. - * Default is {@code MISFIRE_INSTRUCTION_SMART_POLICY}. - * @see org.quartz.CronTrigger#MISFIRE_INSTRUCTION_FIRE_ONCE_NOW - * @see org.quartz.CronTrigger#MISFIRE_INSTRUCTION_DO_NOTHING - * @see org.quartz.Trigger#MISFIRE_INSTRUCTION_SMART_POLICY - */ - public void setMisfireInstructionName(String constantName) { - setMisfireInstruction(constants.asNumber(constantName).intValue()); - } - - /** - * Set a list of TriggerListener names for this job, referring to - * non-global TriggerListeners registered with the Scheduler. - *

A TriggerListener name always refers to the name returned - * by the TriggerListener implementation. - * @see SchedulerFactoryBean#setTriggerListeners - * @see org.quartz.TriggerListener#getName - * @deprecated as of Spring 4.0, since it only works on Quartz 1.x - */ - @Deprecated - public void setTriggerListenerNames(String... names) { - for (String name : names) { - addTriggerListener(name); - } - } - - /** - * Set the start delay in milliseconds. - *

The start delay is added to the current system time (when the bean starts) - * to control the {@link #setStartTime start time} of the trigger. - *

If the start delay is non-zero, it will always - * take precedence over start time. - * @param startDelay the start delay, in milliseconds - */ - public void setStartDelay(long startDelay) { - Assert.state(startDelay >= 0, "Start delay cannot be negative."); - this.startDelay = startDelay; - } - - /** - * Set the JobDetail that this trigger should be associated with. - *

This is typically used with a bean reference if the JobDetail - * is a Spring-managed bean. Alternatively, the trigger can also - * be associated with a job by name and group. - * @see #setJobName - * @see #setJobGroup - */ - public void setJobDetail(JobDetail jobDetail) { - this.jobDetail = jobDetail; - } - - @Override - public JobDetail getJobDetail() { - return this.jobDetail; - } - - @Override - public void setBeanName(String beanName) { - this.beanName = beanName; - } - - - @Override - public void afterPropertiesSet() { - if (getName() == null) { - setName(this.beanName); - } - if (getGroup() == null) { - setGroup(Scheduler.DEFAULT_GROUP); - } - if (this.startDelay > 0 || getStartTime() == null) { - setStartTime(new Date(System.currentTimeMillis() + this.startDelay)); - } - if (getTimeZone() == null) { - setTimeZone(TimeZone.getDefault()); - } - if (this.jobDetail != null) { - setJobName(this.jobDetail.getName()); - setJobGroup(this.jobDetail.getGroup()); - } - } - -} diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/CronTriggerFactoryBean.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/CronTriggerFactoryBean.java index d5ffbcc94b6..666f591e008 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/CronTriggerFactoryBean.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/CronTriggerFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ package org.springframework.scheduling.quartz; -import java.lang.reflect.Method; +import java.text.ParseException; import java.util.Date; import java.util.Map; import java.util.TimeZone; @@ -25,16 +25,13 @@ import org.quartz.CronTrigger; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.Scheduler; +import org.quartz.impl.triggers.CronTriggerImpl; -import org.springframework.beans.BeanWrapper; -import org.springframework.beans.BeanWrapperImpl; -import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.Constants; import org.springframework.util.Assert; -import org.springframework.util.ReflectionUtils; /** * A Spring {@link FactoryBean} for creating a Quartz {@link org.quartz.CronTrigger} @@ -49,9 +46,6 @@ import org.springframework.util.ReflectionUtils; * to automatically register a trigger for the corresponding JobDetail, * instead of registering the JobDetail separately. * - *

NOTE: This FactoryBean works against both Quartz 1.x and Quartz 2.x, - * in contrast to the older {@link CronTriggerBean} class. - * * @author Juergen Hoeller * @since 3.1 * @see #setName @@ -60,7 +54,6 @@ import org.springframework.util.ReflectionUtils; * @see #setJobDetail * @see SchedulerFactoryBean#setTriggers * @see SchedulerFactoryBean#setJobDetails - * @see SimpleTriggerBean */ public class CronTriggerFactoryBean implements FactoryBean, BeanNameAware, InitializingBean { @@ -139,7 +132,6 @@ public class CronTriggerFactoryBean implements FactoryBean, BeanNam * in contrast to objects in the JobDetail's data map. * @param jobDataAsMap Map with String keys and any objects as values * (for example Spring-managed beans) - * @see org.springframework.scheduling.quartz.JobDetailBean#setJobDataAsMap */ public void setJobDataAsMap(Map jobDataAsMap) { this.jobDataMap.putAll(jobDataAsMap); @@ -225,7 +217,7 @@ public class CronTriggerFactoryBean implements FactoryBean, BeanNam @Override - public void afterPropertiesSet() { + public void afterPropertiesSet() throws ParseException { if (this.name == null) { this.name = this.beanName; } @@ -233,7 +225,7 @@ public class CronTriggerFactoryBean implements FactoryBean, BeanNam this.group = Scheduler.DEFAULT_GROUP; } if (this.jobDetail != null) { - this.jobDataMap.put(JobDetailAwareTrigger.JOB_DETAIL_KEY, this.jobDetail); + this.jobDataMap.put("jobDetail", this.jobDetail); } if (this.startDelay > 0 || this.startTime == null) { this.startTime = new Date(System.currentTimeMillis() + this.startDelay); @@ -242,7 +234,6 @@ public class CronTriggerFactoryBean implements FactoryBean, BeanNam this.timeZone = TimeZone.getDefault(); } - /* CronTriggerImpl cti = new CronTriggerImpl(); cti.setName(this.name); cti.setGroup(this.group); @@ -256,42 +247,6 @@ public class CronTriggerFactoryBean implements FactoryBean, BeanNam cti.setMisfireInstruction(this.misfireInstruction); cti.setDescription(this.description); this.cronTrigger = cti; - */ - - Class cronTriggerClass; - Method jobKeyMethod; - try { - cronTriggerClass = getClass().getClassLoader().loadClass("org.quartz.impl.triggers.CronTriggerImpl"); - jobKeyMethod = JobDetail.class.getMethod("getKey"); - } - catch (ClassNotFoundException ex) { - cronTriggerClass = CronTrigger.class; - jobKeyMethod = null; - } - catch (NoSuchMethodException ex) { - throw new IllegalStateException("Incompatible Quartz version"); - } - BeanWrapper bw = new BeanWrapperImpl(cronTriggerClass); - MutablePropertyValues pvs = new MutablePropertyValues(); - pvs.add("name", this.name); - pvs.add("group", this.group); - if (jobKeyMethod != null) { - pvs.add("jobKey", ReflectionUtils.invokeMethod(jobKeyMethod, this.jobDetail)); - } - else { - pvs.add("jobName", this.jobDetail.getName()); - pvs.add("jobGroup", this.jobDetail.getGroup()); - } - pvs.add("jobDataMap", this.jobDataMap); - pvs.add("startTime", this.startTime); - pvs.add("cronExpression", this.cronExpression); - pvs.add("timeZone", this.timeZone); - pvs.add("calendarName", this.calendarName); - pvs.add("priority", this.priority); - pvs.add("misfireInstruction", this.misfireInstruction); - pvs.add("description", this.description); - bw.setPropertyValues(pvs); - this.cronTrigger = (CronTrigger) bw.getWrappedInstance(); } diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/JobDetailAwareTrigger.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/JobDetailAwareTrigger.java deleted file mode 100644 index 3edec2aacba..00000000000 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/JobDetailAwareTrigger.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2002-2012 the original author or authors. - * - * Licensed 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.springframework.scheduling.quartz; - -import org.quartz.JobDetail; - -/** - * Interface to be implemented by Quartz Triggers that are aware - * of the JobDetail object that they are associated with. - * - *

SchedulerFactoryBean will auto-detect Triggers that implement this - * interface and register them for the respective JobDetail accordingly. - * - *

The alternative is to configure a Trigger for a Job name and group: - * This involves the need to register the JobDetail object separately - * with SchedulerFactoryBean. - * - *

NOTE: As of Quartz 2.0, the recommended strategy is to define an - * entry of name "jobDetail" and type JobDetail in the trigger's JobDataMap. - * - * @author Juergen Hoeller - * @since 18.02.2004 - * @see SchedulerFactoryBean#setTriggers - * @see SchedulerFactoryBean#setJobDetails - * @see org.quartz.Trigger#setJobName - * @see org.quartz.Trigger#setJobGroup - */ -public interface JobDetailAwareTrigger { - - /** - * Name of the key for the JobDetail value in the trigger's JobDataMap. - * This is an alternative to implementing the JobDetailAwareTrigger interface. - */ - String JOB_DETAIL_KEY = "jobDetail"; - - /** - * Return the JobDetail that this Trigger is associated with. - * @return the associated JobDetail, or {@code null} if none - */ - JobDetail getJobDetail(); - -} diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/JobDetailBean.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/JobDetailBean.java deleted file mode 100644 index f0d7762225c..00000000000 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/JobDetailBean.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * Licensed 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.springframework.scheduling.quartz; - -import java.util.Map; - -import org.quartz.Job; -import org.quartz.JobDetail; -import org.quartz.Scheduler; - -import org.springframework.beans.factory.BeanNameAware; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; - -/** - * Convenience subclass of Quartz's {@link org.quartz.JobDetail} class, - * making bean-style usage easier. - * - *

{@code JobDetail} itself is already a JavaBean but lacks - * sensible defaults. This class uses the Spring bean name as job name, - * and the Quartz default group ("DEFAULT") as job group if not specified. - * - *

NOTE: This convenience subclass does not work against Quartz 2.0. - * Use Quartz 2.0's native {@code JobDetailImpl} class or the new Quartz 2.0 - * builder API instead. Alternatively, switch to Spring's {@link JobDetailFactoryBean} - * which largely is a drop-in replacement for this class and its properties and - * consistently works against Quartz 1.x as well as Quartz 2.x. - * - * @author Juergen Hoeller - * @since 18.02.2004 - * @see #setName - * @see #setGroup - * @see org.springframework.beans.factory.BeanNameAware - * @see org.quartz.Scheduler#DEFAULT_GROUP - */ -@SuppressWarnings({"serial", "rawtypes"}) -public class JobDetailBean extends JobDetail - implements BeanNameAware, ApplicationContextAware, InitializingBean { - - private Class actualJobClass; - - private String beanName; - - private ApplicationContext applicationContext; - - private String applicationContextJobDataKey; - - - /** - * Overridden to support any job class, to allow a custom JobFactory - * to adapt the given job class to the Quartz Job interface. - * @see SchedulerFactoryBean#setJobFactory - */ - @Override - public void setJobClass(Class jobClass) { - if (jobClass != null && !Job.class.isAssignableFrom(jobClass)) { - super.setJobClass(DelegatingJob.class); - this.actualJobClass = jobClass; - } - else { - super.setJobClass(jobClass); - } - } - - /** - * Overridden to support any job class, to allow a custom JobFactory - * to adapt the given job class to the Quartz Job interface. - */ - @Override - public Class getJobClass() { - return (this.actualJobClass != null ? this.actualJobClass : super.getJobClass()); - } - - /** - * Register objects in the JobDataMap via a given Map. - *

These objects will be available to this Job only, - * in contrast to objects in the SchedulerContext. - *

Note: When using persistent Jobs whose JobDetail will be kept in the - * database, do not put Spring-managed beans or an ApplicationContext - * reference into the JobDataMap but rather into the SchedulerContext. - * @param jobDataAsMap Map with String keys and any objects as values - * (for example Spring-managed beans) - * @see SchedulerFactoryBean#setSchedulerContextAsMap - */ - public void setJobDataAsMap(Map jobDataAsMap) { - getJobDataMap().putAll(jobDataAsMap); - } - - /** - * Set a list of JobListener names for this job, referring to - * non-global JobListeners registered with the Scheduler. - *

A JobListener name always refers to the name returned - * by the JobListener implementation. - * @see SchedulerFactoryBean#setJobListeners - * @see org.quartz.JobListener#getName - * @deprecated as of Spring 4.0, since it only works on Quartz 1.x - */ - @Deprecated - public void setJobListenerNames(String... names) { - for (String name : names) { - addJobListener(name); - } - } - - @Override - public void setBeanName(String beanName) { - this.beanName = beanName; - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } - - /** - * Set the key of an ApplicationContext reference to expose in the JobDataMap, - * for example "applicationContext". Default is none. - * Only applicable when running in a Spring ApplicationContext. - *

In case of a QuartzJobBean, the reference will be applied to the Job - * instance as bean property. An "applicationContext" attribute will correspond - * to a "setApplicationContext" method in that scenario. - *

Note that BeanFactory callback interfaces like ApplicationContextAware - * are not automatically applied to Quartz Job instances, because Quartz - * itself is responsible for the lifecycle of its Jobs. - *

Note: When using persistent job stores where JobDetail contents will - * be kept in the database, do not put an ApplicationContext reference into - * the JobDataMap but rather into the SchedulerContext. - * @see SchedulerFactoryBean#setApplicationContextSchedulerContextKey - * @see org.springframework.context.ApplicationContext - */ - public void setApplicationContextJobDataKey(String applicationContextJobDataKey) { - this.applicationContextJobDataKey = applicationContextJobDataKey; - } - - - @Override - public void afterPropertiesSet() { - if (getName() == null) { - setName(this.beanName); - } - if (getGroup() == null) { - setGroup(Scheduler.DEFAULT_GROUP); - } - if (this.applicationContextJobDataKey != null) { - if (this.applicationContext == null) { - throw new IllegalStateException( - "JobDetailBean needs to be set up in an ApplicationContext " + - "to be able to handle an 'applicationContextJobDataKey'"); - } - getJobDataMap().put(this.applicationContextJobDataKey, this.applicationContext); - } - } - -} diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/JobDetailFactoryBean.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/JobDetailFactoryBean.java index 38d64537a25..50167a90127 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/JobDetailFactoryBean.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/JobDetailFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,10 +21,8 @@ import java.util.Map; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.Scheduler; +import org.quartz.impl.JobDetailImpl; -import org.springframework.beans.BeanWrapper; -import org.springframework.beans.BeanWrapperImpl; -import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; @@ -39,9 +37,6 @@ import org.springframework.context.ApplicationContextAware; * sensible defaults. This class uses the Spring bean name as job name, * and the Quartz default group ("DEFAULT") as job group if not specified. * - *

NOTE: This FactoryBean works against both Quartz 1.x and Quartz 2.x, - * in contrast to the older {@link JobDetailBean} class. - * * @author Juergen Hoeller * @since 3.1 * @see #setName @@ -181,6 +176,7 @@ public class JobDetailFactoryBean @Override + @SuppressWarnings("unchecked") public void afterPropertiesSet() { if (this.name == null) { this.name = this.beanName; @@ -197,35 +193,15 @@ public class JobDetailFactoryBean getJobDataMap().put(this.applicationContextJobDataKey, this.applicationContext); } - /* JobDetailImpl jdi = new JobDetailImpl(); jdi.setName(this.name); jdi.setGroup(this.group); - jdi.setJobClass(this.jobClass); + jdi.setJobClass((Class) this.jobClass); jdi.setJobDataMap(this.jobDataMap); jdi.setDurability(this.durability); + jdi.setRequestsRecovery(this.requestsRecovery); jdi.setDescription(this.description); this.jobDetail = jdi; - */ - - Class jobDetailClass; - try { - jobDetailClass = getClass().getClassLoader().loadClass("org.quartz.impl.JobDetailImpl"); - } - catch (ClassNotFoundException ex) { - jobDetailClass = JobDetail.class; - } - BeanWrapper bw = new BeanWrapperImpl(jobDetailClass); - MutablePropertyValues pvs = new MutablePropertyValues(); - pvs.add("name", this.name); - pvs.add("group", this.group); - pvs.add("jobClass", this.jobClass); - pvs.add("jobDataMap", this.jobDataMap); - pvs.add("durability", this.durability); - pvs.add("requestsRecovery", this.requestsRecovery); - pvs.add("description", this.description); - bw.setPropertyValues(pvs); - this.jobDetail = (JobDetail) bw.getWrappedInstance(); } diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/LocalTaskExecutorThreadPool.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/LocalTaskExecutorThreadPool.java index ec3ccac53fd..03117880b40 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/LocalTaskExecutorThreadPool.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/LocalTaskExecutorThreadPool.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -87,7 +87,7 @@ public class LocalTaskExecutorThreadPool implements ThreadPool { @Override public int blockForAvailableThreads() { - // The present implementation always returns 1, making Quartz (1.6) + // The present implementation always returns 1, making Quartz // always schedule any tasks that it feels like scheduling. // This could be made smarter for specific TaskExecutors, // for example calling {@code getMaximumPoolSize() - getActiveCount()} diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/MethodInvokingJobDetailFactoryBean.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/MethodInvokingJobDetailFactoryBean.java index 2bf9d504dc4..f2381411a30 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/MethodInvokingJobDetailFactoryBean.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/MethodInvokingJobDetailFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,20 +17,17 @@ package org.springframework.scheduling.quartz; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.quartz.JobDataMap; +import org.quartz.DisallowConcurrentExecution; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; +import org.quartz.PersistJobDataAfterExecution; import org.quartz.Scheduler; -import org.quartz.StatefulJob; +import org.quartz.impl.JobDetailImpl; -import org.springframework.beans.BeanUtils; -import org.springframework.beans.BeanWrapper; -import org.springframework.beans.PropertyAccessorFactory; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; @@ -41,7 +38,6 @@ import org.springframework.beans.support.ArgumentConvertingMethodInvoker; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.MethodInvoker; -import org.springframework.util.ReflectionUtils; /** * {@link org.springframework.beans.factory.FactoryBean} that exposes a @@ -67,8 +63,7 @@ import org.springframework.util.ReflectionUtils; * You need to implement your own Quartz Job as a thin wrapper for each case * where you want a persistent job to delegate to a specific service method. * - *

Compatible with Quartz 1.8 as well as Quartz 2.0-2.2, as of Spring 4.0. - * Note: Quartz 1.x support is deprecated - please upgrade to Quartz 2.0+. + *

Compatible with Quartz 2.1.4 and higher, as of Spring 4.1. * * @author Juergen Hoeller * @author Alef Arendsen @@ -81,28 +76,6 @@ import org.springframework.util.ReflectionUtils; public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethodInvoker implements FactoryBean, BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean { - private static Class jobDetailImplClass; - - private static Method setResultMethod; - - static { - try { - jobDetailImplClass = Class.forName("org.quartz.impl.JobDetailImpl"); - } - catch (ClassNotFoundException ex) { - jobDetailImplClass = null; - } - try { - Class jobExecutionContextClass = - QuartzJobBean.class.getClassLoader().loadClass("org.quartz.JobExecutionContext"); - setResultMethod = jobExecutionContextClass.getMethod("setResult", Object.class); - } - catch (Exception ex) { - throw new IllegalStateException("Incompatible Quartz API: " + ex); - } - } - - private String name; private String group = Scheduler.DEFAULT_GROUP; @@ -111,8 +84,6 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod private String targetBeanName; - private String[] jobListenerNames; - private String beanName; private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader(); @@ -125,7 +96,6 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod /** * Set the name of the job. *

Default is the bean name of this FactoryBean. - * @see org.quartz.JobDetail#setName */ public void setName(String name) { this.name = name; @@ -134,7 +104,6 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod /** * Set the group of the job. *

Default is the default group of the Scheduler. - * @see org.quartz.JobDetail#setGroup * @see org.quartz.Scheduler#DEFAULT_GROUP */ public void setGroup(String group) { @@ -142,9 +111,10 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod } /** - * Specify whether or not multiple jobs should be run in a concurrent - * fashion. The behavior when one does not want concurrent jobs to be - * executed is realized through adding the {@link StatefulJob} interface. + * Specify whether or not multiple jobs should be run in a concurrent fashion. + * The behavior when one does not want concurrent jobs to be executed is + * realized through adding the {@code @PersistJobDataAfterExecution} and + * {@code @DisallowConcurrentExecution} markers. * More information on stateful versus stateless jobs can be found * here. *

The default setting is to run jobs concurrently. @@ -165,20 +135,6 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod this.targetBeanName = targetBeanName; } - /** - * Set a list of JobListener names for this job, referring to - * non-global JobListeners registered with the Scheduler. - *

A JobListener name always refers to the name returned - * by the JobListener implementation. - * @see SchedulerFactoryBean#setJobListeners - * @see org.quartz.JobListener#getName - * @deprecated as of Spring 4.0, since it only works on Quartz 1.x - */ - @Deprecated - public void setJobListenerNames(String... names) { - this.jobListenerNames = names; - } - @Override public void setBeanName(String beanName) { this.beanName = beanName; @@ -201,6 +157,7 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod @Override + @SuppressWarnings("unchecked") public void afterPropertiesSet() throws ClassNotFoundException, NoSuchMethodException { prepare(); @@ -211,34 +168,13 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod Class jobClass = (this.concurrent ? MethodInvokingJob.class : StatefulMethodInvokingJob.class); // Build JobDetail instance. - if (jobDetailImplClass != null) { - // Using Quartz 2.0 JobDetailImpl class... - this.jobDetail = (JobDetail) BeanUtils.instantiate(jobDetailImplClass); - BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this.jobDetail); - bw.setPropertyValue("name", name); - bw.setPropertyValue("group", this.group); - bw.setPropertyValue("jobClass", jobClass); - bw.setPropertyValue("durability", true); - ((JobDataMap) bw.getPropertyValue("jobDataMap")).put("methodInvoker", this); - } - else { - // Using Quartz 1.x JobDetail class... - this.jobDetail = new JobDetail(name, this.group, jobClass); - this.jobDetail.setVolatility(true); - this.jobDetail.setDurability(true); - this.jobDetail.getJobDataMap().put("methodInvoker", this); - } - - // Register job listener names. - if (this.jobListenerNames != null) { - for (String jobListenerName : this.jobListenerNames) { - if (jobDetailImplClass != null) { - throw new IllegalStateException("Non-global JobListeners not supported on Quartz 2 - " + - "manually register a Matcher against the Quartz ListenerManager instead"); - } - this.jobDetail.addJobListener(jobListenerName); - } - } + JobDetailImpl jdi = new JobDetailImpl(); + jdi.setName(name); + jdi.setGroup(group); + jdi.setJobClass((Class) jobClass); + jdi.setDurability(true); + jdi.getJobDataMap().put("methodInvoker", this); + this.jobDetail = jdi; postProcessJobDetail(this.jobDetail); } @@ -318,7 +254,7 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { try { - ReflectionUtils.invokeMethod(setResultMethod, context, this.methodInvoker.invoke()); + context.setResult(this.methodInvoker.invoke()); } catch (InvocationTargetException ex) { if (ex.getTargetException() instanceof JobExecutionException) { @@ -343,7 +279,9 @@ public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethod * Quartz checks whether or not jobs are stateful and if so, * won't let jobs interfere with each other. */ - public static class StatefulMethodInvokingJob extends MethodInvokingJob implements StatefulJob { + @PersistJobDataAfterExecution + @DisallowConcurrentExecution + public static class StatefulMethodInvokingJob extends MethodInvokingJob { // No implementation, just an addition of the tag interface StatefulJob // in order to allow stateful method invoking jobs. diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/QuartzJobBean.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/QuartzJobBean.java index c78498f5d00..8f823d24928 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/QuartzJobBean.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/QuartzJobBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,19 +16,14 @@ package org.springframework.scheduling.quartz; -import java.lang.reflect.Method; -import java.util.Map; - import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; -import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.springframework.beans.BeanWrapper; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyAccessorFactory; -import org.springframework.util.ReflectionUtils; /** * Simple implementation of the Quartz Job interface, applying the @@ -43,15 +38,9 @@ import org.springframework.util.ReflectionUtils; * i.e. a method "setMyParam(int)". This will also work for complex * types like business objects etc. * - *

Note: The QuartzJobBean class itself only implements the standard - * Quartz {@link org.quartz.Job} interface. Let your subclass explicitly - * implement the Quartz {@link org.quartz.StatefulJob} interface to - * mark your concrete job bean as stateful. - * - *

Note that as of Spring 2.0 and Quartz 1.5, the preferred way - * to apply dependency injection to Job instances is via a JobFactory: - * that is, to specify {@link SpringBeanJobFactory} as Quartz JobFactory - * (typically via + *

Note that the preferred way to apply dependency injection + * to Job instances is via a JobFactory: that is, to specify + * {@link SpringBeanJobFactory} as Quartz JobFactory (typically via * {@link SchedulerFactoryBean#setJobFactory} SchedulerFactoryBean's "jobFactory" property}). * This allows to implement dependency-injected Quartz Jobs without * a dependency on Spring base classes. @@ -60,33 +49,12 @@ import org.springframework.util.ReflectionUtils; * @since 18.02.2004 * @see org.quartz.JobExecutionContext#getMergedJobDataMap() * @see org.quartz.Scheduler#getContext() - * @see JobDetailBean#setJobDataAsMap - * @see SimpleTriggerBean#setJobDataAsMap - * @see CronTriggerBean#setJobDataAsMap * @see SchedulerFactoryBean#setSchedulerContextAsMap * @see SpringBeanJobFactory * @see SchedulerFactoryBean#setJobFactory */ public abstract class QuartzJobBean implements Job { - private static final Method getSchedulerMethod; - - private static final Method getMergedJobDataMapMethod; - - - static { - try { - Class jobExecutionContextClass = - QuartzJobBean.class.getClassLoader().loadClass("org.quartz.JobExecutionContext"); - getSchedulerMethod = jobExecutionContextClass.getMethod("getScheduler"); - getMergedJobDataMapMethod = jobExecutionContextClass.getMethod("getMergedJobDataMap"); - } - catch (Exception ex) { - throw new IllegalStateException("Incompatible Quartz API: " + ex); - } - } - - /** * This implementation applies the passed-in job data map as bean property * values, and delegates to {@code executeInternal} afterwards. @@ -95,14 +63,10 @@ public abstract class QuartzJobBean implements Job { @Override public final void execute(JobExecutionContext context) throws JobExecutionException { try { - // Reflectively adapting to differences between Quartz 1.x and Quartz 2.0... - Scheduler scheduler = (Scheduler) ReflectionUtils.invokeMethod(getSchedulerMethod, context); - Map mergedJobDataMap = (Map) ReflectionUtils.invokeMethod(getMergedJobDataMapMethod, context); - BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); MutablePropertyValues pvs = new MutablePropertyValues(); - pvs.addPropertyValues(scheduler.getContext()); - pvs.addPropertyValues(mergedJobDataMap); + pvs.addPropertyValues(context.getScheduler().getContext()); + pvs.addPropertyValues(context.getMergedJobDataMap()); bw.setPropertyValues(pvs, true); } catch (SchedulerException ex) { diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessor.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessor.java index a86a4b054a7..d93aa31f5f1 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessor.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessor.java @@ -16,7 +16,6 @@ package org.springframework.scheduling.quartz; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; @@ -28,6 +27,7 @@ import org.apache.commons.logging.LogFactory; import org.quartz.Calendar; import org.quartz.JobDetail; import org.quartz.JobListener; +import org.quartz.ListenerManager; import org.quartz.ObjectAlreadyExistsException; import org.quartz.Scheduler; import org.quartz.SchedulerException; @@ -43,7 +43,6 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionException; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; -import org.springframework.util.ReflectionUtils; /** * Common base class for accessing a Quartz Scheduler, i.e. for registering jobs, @@ -52,8 +51,7 @@ import org.springframework.util.ReflectionUtils; *

For concrete usage, check out the {@link SchedulerFactoryBean} and * {@link SchedulerAccessorBean} classes. * - *

Compatible with Quartz 1.8 as well as Quartz 2.0-2.2, as of Spring 4.0. - * Note: Quartz 1.x support is deprecated - please upgrade to Quartz 2.0+. + *

Compatible with Quartz 2.1.4 and higher, as of Spring 4.1. * * @author Juergen Hoeller * @author Stephane Nicoll @@ -61,23 +59,6 @@ import org.springframework.util.ReflectionUtils; */ public abstract class SchedulerAccessor implements ResourceLoaderAware { - private static Class jobKeyClass; - - private static Class triggerKeyClass; - - static { - // Quartz 2.0 job/trigger key available? - try { - jobKeyClass = Class.forName("org.quartz.JobKey"); - triggerKeyClass = Class.forName("org.quartz.TriggerKey"); - } - catch (ClassNotFoundException ex) { - jobKeyClass = null; - triggerKeyClass = null; - } - } - - protected final Log logger = LogFactory.getLog(getClass()); private boolean overwriteExistingJobs = false; @@ -94,24 +75,13 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware { private JobListener[] globalJobListeners; - private JobListener[] jobListeners; - private TriggerListener[] globalTriggerListeners; - private TriggerListener[] triggerListeners; - private PlatformTransactionManager transactionManager; protected ResourceLoader resourceLoader; - public SchedulerAccessor() { - if (jobKeyClass == null && logger.isInfoEnabled()) { - logger.info("Spring's Quartz 1.x support is deprecated - please upgrade to Quartz 2.0+"); - } - } - - /** * Set whether any jobs defined on this SchedulerFactoryBean should overwrite * existing job definitions. Default is "false", to not overwrite already @@ -151,8 +121,6 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware { * in combination with the Trigger. * @see #setTriggers * @see org.quartz.JobDetail - * @see JobDetailBean - * @see JobDetailAwareTrigger */ public void setJobDetails(JobDetail... jobDetails) { // Use modifiable ArrayList here, to allow for further adding of @@ -180,15 +148,11 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware { * "jobDetails" property of this FactoryBean. * @see #setJobDetails * @see org.quartz.JobDetail - * @see JobDetailAwareTrigger - * @see CronTriggerBean - * @see SimpleTriggerBean */ public void setTriggers(Trigger... triggers) { this.triggers = Arrays.asList(triggers); } - /** * Specify Quartz SchedulerListeners to be registered with the Scheduler. */ @@ -204,21 +168,6 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware { this.globalJobListeners = globalJobListeners; } - /** - * Specify named Quartz JobListeners to be registered with the Scheduler. - * Such JobListeners will only apply to Jobs that explicitly activate - * them via their name. - *

Note that non-global JobListeners are not supported on Quartz 2.x - - * manually register a Matcher against the Quartz ListenerManager instead. - * @see org.quartz.JobListener#getName - * @see JobDetailBean#setJobListenerNames - * @deprecated as of Spring 4.0, since it only works on Quartz 1.x - */ - @Deprecated - public void setJobListeners(JobListener... jobListeners) { - this.jobListeners = jobListeners; - } - /** * Specify global Quartz TriggerListeners to be registered with the Scheduler. * Such TriggerListeners will apply to all Triggers in the Scheduler. @@ -227,23 +176,6 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware { this.globalTriggerListeners = globalTriggerListeners; } - /** - * Specify named Quartz TriggerListeners to be registered with the Scheduler. - * Such TriggerListeners will only apply to Triggers that explicitly activate - * them via their name. - *

Note that non-global TriggerListeners are not supported on Quartz 2.x - - * manually register a Matcher against the Quartz ListenerManager instead. - * @see org.quartz.TriggerListener#getName - * @see CronTriggerBean#setTriggerListenerNames - * @see SimpleTriggerBean#setTriggerListenerNames - * @deprecated as of Spring 4.0, since it only works on Quartz 1.x - */ - @Deprecated - public void setTriggerListeners(TriggerListener... triggerListeners) { - this.triggerListeners = triggerListeners; - } - - /** * Set the transaction manager to be used for registering jobs and triggers * that are defined by this SchedulerFactoryBean. Default is none; setting @@ -338,7 +270,7 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware { * @see #setOverwriteExistingJobs */ private boolean addJobToScheduler(JobDetail jobDetail) throws SchedulerException { - if (this.overwriteExistingJobs || !jobDetailExists(jobDetail)) { + if (this.overwriteExistingJobs || getScheduler().getJobDetail(jobDetail.getKey()) == null) { getScheduler().addJob(jobDetail, true); return true; } @@ -356,10 +288,10 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware { * @see #setOverwriteExistingJobs */ private boolean addTriggerToScheduler(Trigger trigger) throws SchedulerException { - boolean triggerExists = triggerExists(trigger); + boolean triggerExists = (getScheduler().getTrigger(trigger.getKey()) != null); if (!triggerExists || this.overwriteExistingJobs) { // Check if the Trigger is aware of an associated JobDetail. - JobDetail jobDetail = findJobDetail(trigger); + JobDetail jobDetail = (JobDetail) trigger.getJobDataMap().remove("jobDetail"); if (jobDetail != null) { // Automatically register the JobDetail too. if (!this.jobDetails.contains(jobDetail) && addJobToScheduler(jobDetail)) { @@ -376,12 +308,12 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware { ex.getMessage() + " - can safely be ignored"); } if (this.overwriteExistingJobs) { - rescheduleJob(trigger); + getScheduler().rescheduleJob(trigger.getKey(), trigger); } } } else { - rescheduleJob(trigger); + getScheduler().rescheduleJob(trigger.getKey(), trigger); } return true; } @@ -390,160 +322,25 @@ public abstract class SchedulerAccessor implements ResourceLoaderAware { } } - private JobDetail findJobDetail(Trigger trigger) { - if (trigger instanceof JobDetailAwareTrigger) { - return ((JobDetailAwareTrigger) trigger).getJobDetail(); - } - else { - try { - Map jobDataMap = - (Map) ReflectionUtils.invokeMethod(Trigger.class.getMethod("getJobDataMap"), trigger); - return (JobDetail) jobDataMap.remove(JobDetailAwareTrigger.JOB_DETAIL_KEY); - } - catch (NoSuchMethodException ex) { - throw new IllegalStateException("Inconsistent Quartz API: " + ex); - } - } - } - - - // Reflectively adapting to differences between Quartz 1.x and Quartz 2.0... - private boolean jobDetailExists(JobDetail jobDetail) throws SchedulerException { - if (jobKeyClass != null) { - try { - Method getJobDetail = Scheduler.class.getMethod("getJobDetail", jobKeyClass); - Object key = ReflectionUtils.invokeMethod(JobDetail.class.getMethod("getKey"), jobDetail); - return (ReflectionUtils.invokeMethod(getJobDetail, getScheduler(), key) != null); - } - catch (NoSuchMethodException ex) { - throw new IllegalStateException("Inconsistent Quartz 2.0 API: " + ex); - } - } - else { - return (getScheduler().getJobDetail(jobDetail.getName(), jobDetail.getGroup()) != null); - } - } - - // Reflectively adapting to differences between Quartz 1.x and Quartz 2.0... - private boolean triggerExists(Trigger trigger) throws SchedulerException { - if (triggerKeyClass != null) { - try { - Method getTrigger = Scheduler.class.getMethod("getTrigger", triggerKeyClass); - Object key = ReflectionUtils.invokeMethod(Trigger.class.getMethod("getKey"), trigger); - return (ReflectionUtils.invokeMethod(getTrigger, getScheduler(), key) != null); - } - catch (NoSuchMethodException ex) { - throw new IllegalStateException("Inconsistent Quartz 2.0 API: " + ex); - } - } - else { - return (getScheduler().getTrigger(trigger.getName(), trigger.getGroup()) != null); - } - } - - // Reflectively adapting to differences between Quartz 1.x and Quartz 2.0... - private void rescheduleJob(Trigger trigger) throws SchedulerException { - if (triggerKeyClass != null) { - try { - Method rescheduleJob = Scheduler.class.getMethod("rescheduleJob", triggerKeyClass, Trigger.class); - Object key = ReflectionUtils.invokeMethod(Trigger.class.getMethod("getKey"), trigger); - ReflectionUtils.invokeMethod(rescheduleJob, getScheduler(), key, trigger); - } - catch (NoSuchMethodException ex) { - throw new IllegalStateException("Inconsistent Quartz 2.0 API: " + ex); - } - } - else { - getScheduler().rescheduleJob(trigger.getName(), trigger.getGroup(), trigger); - } - } - - /** * Register all specified listeners with the Scheduler. */ protected void registerListeners() throws SchedulerException { - Object target; - boolean quartz2; - try { - Method getListenerManager = Scheduler.class.getMethod("getListenerManager"); - target = ReflectionUtils.invokeMethod(getListenerManager, getScheduler()); - quartz2 = true; - } - catch (NoSuchMethodException ex) { - target = getScheduler(); - quartz2 = false; - } - Class targetClass = target.getClass(); - - try { - if (this.schedulerListeners != null) { - Method addSchedulerListener = targetClass.getMethod("addSchedulerListener", SchedulerListener.class); - for (SchedulerListener listener : this.schedulerListeners) { - ReflectionUtils.invokeMethod(addSchedulerListener, target, listener); - } + ListenerManager listenerManager = getScheduler().getListenerManager(); + if (this.schedulerListeners != null) { + for (SchedulerListener listener : this.schedulerListeners) { + listenerManager.addSchedulerListener(listener); } - if (this.globalJobListeners != null) { - Method addJobListener; - if (quartz2) { - // addJobListener(JobListener) only introduced as late as Quartz 2.2, so we need - // to fall back to the Quartz 2.0/2.1 compatible variant with an empty matchers List - addJobListener = targetClass.getMethod("addJobListener", JobListener.class, List.class); - } - else { - addJobListener = targetClass.getMethod("addGlobalJobListener", JobListener.class); - } - for (JobListener listener : this.globalJobListeners) { - if (quartz2) { - List emptyMatchers = new LinkedList(); - ReflectionUtils.invokeMethod(addJobListener, target, listener, emptyMatchers); - } - else { - ReflectionUtils.invokeMethod(addJobListener, target, listener); - } - } - } - if (this.jobListeners != null) { - for (JobListener listener : this.jobListeners) { - if (quartz2) { - throw new IllegalStateException("Non-global JobListeners not supported on Quartz 2 - " + - "manually register a Matcher against the Quartz ListenerManager instead"); - } - getScheduler().addJobListener(listener); - } - } - if (this.globalTriggerListeners != null) { - Method addTriggerListener; - if (quartz2) { - // addTriggerListener(TriggerListener) only introduced as late as Quartz 2.2, so we need - // to fall back to the Quartz 2.0/2.1 compatible variant with an empty matchers List - addTriggerListener = targetClass.getMethod("addTriggerListener", TriggerListener.class, List.class); - } - else { - addTriggerListener = targetClass.getMethod("addGlobalTriggerListener", TriggerListener.class); - } - for (TriggerListener listener : this.globalTriggerListeners) { - if (quartz2) { - List emptyMatchers = new LinkedList(); - ReflectionUtils.invokeMethod(addTriggerListener, target, listener, emptyMatchers); - } - else { - ReflectionUtils.invokeMethod(addTriggerListener, target, listener); - } - } - } - if (this.triggerListeners != null) { - for (TriggerListener listener : this.triggerListeners) { - if (quartz2) { - throw new IllegalStateException("Non-global TriggerListeners not supported on Quartz 2 - " + - "manually register a Matcher against the Quartz ListenerManager instead"); - } - getScheduler().addTriggerListener(listener); - } + } + if (this.globalJobListeners != null) { + for (JobListener listener : this.globalJobListeners) { + listenerManager.addJobListener(listener); } } - catch (NoSuchMethodException ex) { - throw new IllegalStateException("Expected Quartz API not present: " + ex); + if (this.globalTriggerListeners != null) { + for (TriggerListener listener : this.globalTriggerListeners) { + listenerManager.addTriggerListener(listener); + } } } diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessorBean.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessorBean.java index 5bb0a6553c9..8094917c394 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessorBean.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessorBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,8 +29,7 @@ import org.springframework.beans.factory.ListableBeanFactory; * Spring bean-style class for accessing a Quartz Scheduler, i.e. for registering jobs, * triggers and listeners on a given {@link org.quartz.Scheduler} instance. * - *

Compatible with Quartz 1.8 as well as Quartz 2.0-2.2, as of Spring 4.0. - * Note: Quartz 1.x support is deprecated - please upgrade to Quartz 2.0+. + *

Compatible with Quartz 2.1.4 and higher, as of Spring 4.1. * * @author Juergen Hoeller * @since 2.5.6 diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerFactoryBean.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerFactoryBean.java index 6ba38dcaf98..3319ce54362 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerFactoryBean.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -75,8 +75,7 @@ import org.springframework.util.CollectionUtils; * automatically apply to Scheduler operations performed within those scopes. * Alternatively, you may add transactional advice for the Scheduler itself. * - *

Compatible with Quartz 1.8 as well as Quartz 2.0-2.2, as of Spring 4.0. - * Note: Quartz 1.x support is deprecated - please upgrade to Quartz 2.0+. + *

Compatible with Quartz 2.1.4 and higher, as of Spring 4.1. * * @author Juergen Hoeller * @since 18.02.2004 diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerBean.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerBean.java deleted file mode 100644 index 089243186b6..00000000000 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerBean.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * Licensed 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.springframework.scheduling.quartz; - -import java.util.Date; -import java.util.Map; - -import org.quartz.JobDetail; -import org.quartz.Scheduler; -import org.quartz.SimpleTrigger; - -import org.springframework.beans.factory.BeanNameAware; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.core.Constants; - -/** - * Convenience subclass of Quartz's {@link org.quartz.SimpleTrigger} class, - * making bean-style usage easier. - * - *

{@code SimpleTrigger} itself is already a JavaBean but lacks sensible defaults. - * This class uses the Spring bean name as job name, the Quartz default group - * ("DEFAULT") as job group, the current time as start time, and indefinite - * repetition, if not specified. - * - *

This class will also register the trigger with the job name and group of - * a given {@link org.quartz.JobDetail}. This allows {@link SchedulerFactoryBean} - * to automatically register a trigger for the corresponding JobDetail, - * instead of registering the JobDetail separately. - * - *

NOTE: This convenience subclass does not work against Quartz 2.0. - * Use Quartz 2.0's native {@code JobDetailImpl} class or the new Quartz 2.0 - * builder API instead. Alternatively, switch to Spring's {@link SimpleTriggerFactoryBean} - * which largely is a drop-in replacement for this class and its properties and - * consistently works against Quartz 1.x as well as Quartz 2.x. - * - * @author Juergen Hoeller - * @since 18.02.2004 - * @see #setName - * @see #setGroup - * @see #setStartTime - * @see #setJobName - * @see #setJobGroup - * @see #setJobDetail - * @see SchedulerFactoryBean#setTriggers - * @see SchedulerFactoryBean#setJobDetails - * @see CronTriggerBean - */ -@SuppressWarnings("serial") -public class SimpleTriggerBean extends SimpleTrigger - implements JobDetailAwareTrigger, BeanNameAware, InitializingBean { - - /** Constants for the SimpleTrigger class */ - private static final Constants constants = new Constants(SimpleTrigger.class); - - - private long startDelay = 0; - - private JobDetail jobDetail; - - private String beanName; - - - public SimpleTriggerBean() { - setRepeatCount(REPEAT_INDEFINITELY); - } - - /** - * Register objects in the JobDataMap via a given Map. - *

These objects will be available to this Trigger only, - * in contrast to objects in the JobDetail's data map. - * @param jobDataAsMap Map with String keys and any objects as values - * (for example Spring-managed beans) - * @see JobDetailBean#setJobDataAsMap - */ - public void setJobDataAsMap(Map jobDataAsMap) { - getJobDataMap().putAll(jobDataAsMap); - } - - /** - * Set the misfire instruction via the name of the corresponding - * constant in the {@link org.quartz.SimpleTrigger} class. - * Default is {@code MISFIRE_INSTRUCTION_SMART_POLICY}. - * @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_FIRE_NOW - * @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT - * @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT - * @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT - * @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT - * @see org.quartz.Trigger#MISFIRE_INSTRUCTION_SMART_POLICY - */ - public void setMisfireInstructionName(String constantName) { - setMisfireInstruction(constants.asNumber(constantName).intValue()); - } - - /** - * Set a list of TriggerListener names for this job, referring to - * non-global TriggerListeners registered with the Scheduler. - *

A TriggerListener name always refers to the name returned - * by the TriggerListener implementation. - * @see SchedulerFactoryBean#setTriggerListeners - * @see org.quartz.TriggerListener#getName - * @deprecated as of Spring 4.0, since it only works on Quartz 1.x - */ - @Deprecated - public void setTriggerListenerNames(String... names) { - for (String name : names) { - addTriggerListener(name); - } - } - - /** - * Set the start delay in milliseconds. - *

The start delay is added to the current system time (when the bean starts) - * to control the {@link #setStartTime start time} of the trigger. - *

If the start delay is non-zero, it will always - * take precedence over start time. - * @param startDelay the start delay, in milliseconds - */ - public void setStartDelay(long startDelay) { - this.startDelay = startDelay; - } - - /** - * Set the JobDetail that this trigger should be associated with. - *

This is typically used with a bean reference if the JobDetail - * is a Spring-managed bean. Alternatively, the trigger can also - * be associated with a job by name and group. - * @see #setJobName - * @see #setJobGroup - */ - public void setJobDetail(JobDetail jobDetail) { - this.jobDetail = jobDetail; - } - - @Override - public JobDetail getJobDetail() { - return this.jobDetail; - } - - @Override - public void setBeanName(String beanName) { - this.beanName = beanName; - } - - - @Override - public void afterPropertiesSet() { - if (getName() == null) { - setName(this.beanName); - } - if (getGroup() == null) { - setGroup(Scheduler.DEFAULT_GROUP); - } - if (this.startDelay > 0 || getStartTime() == null) { - setStartTime(new Date(System.currentTimeMillis() + this.startDelay)); - } - if (this.jobDetail != null) { - setJobName(this.jobDetail.getName()); - setJobGroup(this.jobDetail.getGroup()); - } - } - -} diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBean.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBean.java index 2215ac7e667..573fba98c24 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBean.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package org.springframework.scheduling.quartz; -import java.lang.reflect.Method; import java.util.Date; import java.util.Map; @@ -24,16 +23,13 @@ import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SimpleTrigger; +import org.quartz.impl.triggers.SimpleTriggerImpl; -import org.springframework.beans.BeanWrapper; -import org.springframework.beans.BeanWrapperImpl; -import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.Constants; import org.springframework.util.Assert; -import org.springframework.util.ReflectionUtils; /** * A Spring {@link FactoryBean} for creating a Quartz {@link org.quartz.SimpleTrigger} @@ -48,9 +44,6 @@ import org.springframework.util.ReflectionUtils; * to automatically register a trigger for the corresponding JobDetail, * instead of registering the JobDetail separately. * - *

NOTE: This FactoryBean works against both Quartz 1.x and Quartz 2.x, - * in contrast to the older {@link SimpleTriggerBean} class. - * * @author Juergen Hoeller * @since 3.1 * @see #setName @@ -59,7 +52,6 @@ import org.springframework.util.ReflectionUtils; * @see #setJobDetail * @see SchedulerFactoryBean#setTriggers * @see SchedulerFactoryBean#setJobDetails - * @see CronTriggerBean */ public class SimpleTriggerFactoryBean implements FactoryBean, BeanNameAware, InitializingBean { @@ -136,7 +128,6 @@ public class SimpleTriggerFactoryBean implements FactoryBean, Bea * in contrast to objects in the JobDetail's data map. * @param jobDataAsMap Map with String keys and any objects as values * (for example Spring-managed beans) - * @see org.springframework.scheduling.quartz.JobDetailBean#setJobDataAsMap */ public void setJobDataAsMap(Map jobDataAsMap) { this.jobDataMap.putAll(jobDataAsMap); @@ -228,13 +219,12 @@ public class SimpleTriggerFactoryBean implements FactoryBean, Bea this.group = Scheduler.DEFAULT_GROUP; } if (this.jobDetail != null) { - this.jobDataMap.put(JobDetailAwareTrigger.JOB_DETAIL_KEY, this.jobDetail); + this.jobDataMap.put("jobDetail", this.jobDetail); } if (this.startDelay > 0 || this.startTime == null) { this.startTime = new Date(System.currentTimeMillis() + this.startDelay); } - /* SimpleTriggerImpl sti = new SimpleTriggerImpl(); sti.setName(this.name); sti.setGroup(this.group); @@ -245,43 +235,8 @@ public class SimpleTriggerFactoryBean implements FactoryBean, Bea sti.setRepeatCount(this.repeatCount); sti.setPriority(this.priority); sti.setMisfireInstruction(this.misfireInstruction); - cti.setDescription(this.description); + sti.setDescription(this.description); this.simpleTrigger = sti; - */ - - Class simpleTriggerClass; - Method jobKeyMethod; - try { - simpleTriggerClass = getClass().getClassLoader().loadClass("org.quartz.impl.triggers.SimpleTriggerImpl"); - jobKeyMethod = JobDetail.class.getMethod("getKey"); - } - catch (ClassNotFoundException ex) { - simpleTriggerClass = SimpleTrigger.class; - jobKeyMethod = null; - } - catch (NoSuchMethodException ex) { - throw new IllegalStateException("Incompatible Quartz version"); - } - BeanWrapper bw = new BeanWrapperImpl(simpleTriggerClass); - MutablePropertyValues pvs = new MutablePropertyValues(); - pvs.add("name", this.name); - pvs.add("group", this.group); - if (jobKeyMethod != null) { - pvs.add("jobKey", ReflectionUtils.invokeMethod(jobKeyMethod, this.jobDetail)); - } - else { - pvs.add("jobName", this.jobDetail.getName()); - pvs.add("jobGroup", this.jobDetail.getGroup()); - } - pvs.add("jobDataMap", this.jobDataMap); - pvs.add("startTime", this.startTime); - pvs.add("repeatInterval", this.repeatInterval); - pvs.add("repeatCount", this.repeatCount); - pvs.add("priority", this.priority); - pvs.add("misfireInstruction", this.misfireInstruction); - pvs.add("description", this.description); - bw.setPropertyValues(pvs); - this.simpleTrigger = (SimpleTrigger) bw.getWrappedInstance(); } diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SpringBeanJobFactory.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SpringBeanJobFactory.java index 7cba4564968..6a0f737e607 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SpringBeanJobFactory.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SpringBeanJobFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,29 +16,24 @@ package org.springframework.scheduling.quartz; -import java.lang.reflect.Method; - -import org.quartz.JobDataMap; import org.quartz.SchedulerContext; import org.quartz.spi.TriggerFiredBundle; import org.springframework.beans.BeanWrapper; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyAccessorFactory; -import org.springframework.util.ReflectionUtils; /** * Subclass of {@link AdaptableJobFactory} that also supports Spring-style * dependency injection on bean properties. This is essentially the direct - * equivalent of Spring's {@link QuartzJobBean} in the shape of a - * Quartz 1.5 {@link org.quartz.spi.JobFactory}. + * equivalent of Spring's {@link QuartzJobBean} in the shape of a Quartz + * {@link org.quartz.spi.JobFactory}. * *

Applies scheduler context, job data map and trigger data map entries * as bean property values. If no matching bean property is found, the entry * is by default simply ignored. This is analogous to QuartzJobBean's behavior. * - *

Compatible with Quartz 1.8 as well as Quartz 2.0-2.2, as of Spring 4.0. - * Note: Quartz 1.x support is deprecated - please upgrade to Quartz 2.0+. + *

Compatible with Quartz 2.1.4 and higher, as of Spring 4.1. * * @author Juergen Hoeller * @since 2.0 @@ -83,8 +78,8 @@ public class SpringBeanJobFactory extends AdaptableJobFactory implements Schedul if (this.schedulerContext != null) { pvs.addPropertyValues(this.schedulerContext); } - pvs.addPropertyValues(getJobDetailDataMap(bundle)); - pvs.addPropertyValues(getTriggerDataMap(bundle)); + pvs.addPropertyValues(bundle.getJobDetail().getJobDataMap()); + pvs.addPropertyValues(bundle.getTrigger().getJobDataMap()); if (this.ignoredUnknownProperties != null) { for (String propName : this.ignoredUnknownProperties) { if (pvs.contains(propName) && !bw.isWritableProperty(propName)) { @@ -112,20 +107,4 @@ public class SpringBeanJobFactory extends AdaptableJobFactory implements Schedul return (!(jobObject instanceof QuartzJobBean)); } - // Reflectively adapting to differences between Quartz 1.x and Quartz 2.0... - private JobDataMap getJobDetailDataMap(TriggerFiredBundle bundle) throws Exception { - Method getJobDetail = bundle.getClass().getMethod("getJobDetail"); - Object jobDetail = ReflectionUtils.invokeMethod(getJobDetail, bundle); - Method getJobDataMap = jobDetail.getClass().getMethod("getJobDataMap"); - return (JobDataMap) ReflectionUtils.invokeMethod(getJobDataMap, jobDetail); - } - - // Reflectively adapting to differences between Quartz 1.x and Quartz 2.0... - private JobDataMap getTriggerDataMap(TriggerFiredBundle bundle) throws Exception { - Method getTrigger = bundle.getClass().getMethod("getTrigger"); - Object trigger = ReflectionUtils.invokeMethod(getTrigger, bundle); - Method getJobDataMap = trigger.getClass().getMethod("getJobDataMap"); - return (JobDataMap) ReflectionUtils.invokeMethod(getJobDataMap, trigger); - } - } diff --git a/spring-context-support/src/test/java/org/springframework/scheduling/quartz/CronTriggerBeanTests.java b/spring-context-support/src/test/java/org/springframework/scheduling/quartz/CronTriggerBeanTests.java deleted file mode 100644 index 8ccc9c5b697..00000000000 --- a/spring-context-support/src/test/java/org/springframework/scheduling/quartz/CronTriggerBeanTests.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2002-2012 the original author or authors. - * - * Licensed 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.springframework.scheduling.quartz; - -import java.util.Date; -import java.util.Calendar; - -import org.junit.Test; -import static org.junit.Assert.assertEquals; - -/** @author Rob Harrop */ -public class CronTriggerBeanTests { - - @Test(expected = IllegalStateException.class) - public void testInvalidStartDelay() { - createTriggerBean().setStartDelay(-1); - } - - @Test - public void testStartTime() throws Exception { - CronTriggerBean bean = createTriggerBean(); - Date startTime = new Date(System.currentTimeMillis() + 1234L); - bean.setStartTime(startTime); - bean.afterPropertiesSet(); - assertTimeEquals(startTime, bean.getStartTime()); - } - - @Test - public void testStartDelay() throws Exception { - CronTriggerBean bean = createTriggerBean(); - long startDelay = 1234L; - Date startTime = new Date(System.currentTimeMillis() + startDelay); - bean.setStartDelay(startDelay); - bean.afterPropertiesSet(); - assertTimeEquals(startTime, bean.getStartTime()); - } - - @Test - public void testStartDelayOverridesStartTime() throws Exception { - CronTriggerBean bean = createTriggerBean(); - long startDelay = 1234L; - Date startTime = new Date(System.currentTimeMillis() + startDelay); - bean.setStartTime(new Date(System.currentTimeMillis() + 123456789L)); - bean.setStartDelay(startDelay); - bean.afterPropertiesSet(); - assertTimeEquals(startTime, bean.getStartTime()); - } - - private CronTriggerBean createTriggerBean() { - CronTriggerBean triggerBean = new CronTriggerBean(); - triggerBean.setName("test"); - return triggerBean; - } - - private void assertTimeEquals(Date a, Date b) { - Calendar ca = Calendar.getInstance(); - ca.setTime(a); - ca.set(Calendar.MILLISECOND, 0); - Calendar cb = Calendar.getInstance(); - cb.setTime(b); - cb.set(Calendar.MILLISECOND, 0); - assertEquals(ca, cb); - } - -} diff --git a/spring-context-support/src/test/java/org/springframework/scheduling/quartz/QuartzSupportTests.java b/spring-context-support/src/test/java/org/springframework/scheduling/quartz/QuartzSupportTests.java index 5f4d74d8ad4..97d8c3b4032 100644 --- a/spring-context-support/src/test/java/org/springframework/scheduling/quartz/QuartzSupportTests.java +++ b/spring-context-support/src/test/java/org/springframework/scheduling/quartz/QuartzSupportTests.java @@ -16,42 +16,29 @@ package org.springframework.scheduling.quartz; -import java.util.Date; import java.util.HashMap; import java.util.Map; - import javax.sql.DataSource; -import org.junit.Ignore; import org.junit.Test; -import org.quartz.CronTrigger; import org.quartz.Job; -import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; -import org.quartz.JobListener; -import org.quartz.ObjectAlreadyExistsException; import org.quartz.Scheduler; import org.quartz.SchedulerContext; -import org.quartz.SchedulerException; import org.quartz.SchedulerFactory; -import org.quartz.SchedulerListener; -import org.quartz.SimpleTrigger; -import org.quartz.Trigger; -import org.quartz.TriggerListener; +import org.quartz.impl.JobDetailImpl; import org.quartz.impl.SchedulerRepository; -import org.quartz.spi.JobFactory; + import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.beans.factory.support.StaticListableBeanFactory; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.StaticApplicationContext; import org.springframework.core.task.TaskExecutor; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.tests.Assume; import org.springframework.tests.TestGroup; -import org.springframework.tests.context.TestMethodInvokingTask; import org.springframework.tests.sample.beans.TestBean; import static org.junit.Assert.*; @@ -68,473 +55,6 @@ import static org.mockito.BDDMockito.*; */ public class QuartzSupportTests { - @Test - public void testSchedulerFactoryBean() throws Exception { - doTestSchedulerFactoryBean(false, false); - } - - @Test - public void testSchedulerFactoryBeanWithExplicitJobDetail() throws Exception { - doTestSchedulerFactoryBean(true, false); - } - - @Test - public void testSchedulerFactoryBeanWithPrototypeJob() throws Exception { - doTestSchedulerFactoryBean(false, true); - } - - private void doTestSchedulerFactoryBean(boolean explicitJobDetail, boolean prototypeJob) throws Exception { - TestBean tb = new TestBean("tb", 99); - JobDetailBean jobDetail0 = new JobDetailBean(); - jobDetail0.setJobClass(Job.class); - jobDetail0.setBeanName("myJob0"); - Map jobData = new HashMap(); - jobData.put("testBean", tb); - jobDetail0.setJobDataAsMap(jobData); - jobDetail0.afterPropertiesSet(); - assertEquals(tb, jobDetail0.getJobDataMap().get("testBean")); - - CronTriggerBean trigger0 = new CronTriggerBean(); - trigger0.setBeanName("myTrigger0"); - trigger0.setJobDetail(jobDetail0); - trigger0.setCronExpression("0/1 * * * * ?"); - trigger0.afterPropertiesSet(); - - TestMethodInvokingTask task1 = new TestMethodInvokingTask(); - MethodInvokingJobDetailFactoryBean mijdfb = new MethodInvokingJobDetailFactoryBean(); - mijdfb.setBeanName("myJob1"); - if (prototypeJob) { - StaticListableBeanFactory beanFactory = new StaticListableBeanFactory(); - beanFactory.addBean("task", task1); - mijdfb.setTargetBeanName("task"); - mijdfb.setBeanFactory(beanFactory); - } - else { - mijdfb.setTargetObject(task1); - } - mijdfb.setTargetMethod("doSomething"); - mijdfb.afterPropertiesSet(); - JobDetail jobDetail1 = mijdfb.getObject(); - - SimpleTriggerBean trigger1 = new SimpleTriggerBean(); - trigger1.setBeanName("myTrigger1"); - trigger1.setJobDetail(jobDetail1); - trigger1.setStartDelay(0); - trigger1.setRepeatInterval(20); - trigger1.afterPropertiesSet(); - - final Scheduler scheduler = mock(Scheduler.class); - given(scheduler.getContext()).willReturn(new SchedulerContext()); - given(scheduler.scheduleJob(trigger0)).willReturn(new Date()); - given(scheduler.scheduleJob(trigger1)).willReturn(new Date()); - - SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean() { - @Override - protected Scheduler createScheduler(SchedulerFactory schedulerFactory, String schedulerName) { - return scheduler; - } - }; - schedulerFactoryBean.setJobFactory(null); - Map schedulerContext = new HashMap(); - schedulerContext.put("otherTestBean", tb); - schedulerFactoryBean.setSchedulerContextAsMap(schedulerContext); - if (explicitJobDetail) { - schedulerFactoryBean.setJobDetails(new JobDetail[] {jobDetail0}); - } - schedulerFactoryBean.setTriggers(new Trigger[] {trigger0, trigger1}); - try { - schedulerFactoryBean.afterPropertiesSet(); - schedulerFactoryBean.start(); - } - finally { - schedulerFactoryBean.destroy(); - } - - verify(scheduler).getJobDetail("myJob0", Scheduler.DEFAULT_GROUP); - verify(scheduler).getJobDetail("myJob1", Scheduler.DEFAULT_GROUP); - verify(scheduler).getTrigger("myTrigger0", Scheduler.DEFAULT_GROUP); - verify(scheduler).getTrigger("myTrigger1", Scheduler.DEFAULT_GROUP); - verify(scheduler).addJob(jobDetail0, true); - verify(scheduler).addJob(jobDetail1, true); - verify(scheduler).start(); - verify(scheduler).shutdown(false); - } - - @Test - public void testSchedulerFactoryBeanWithExistingJobs() throws Exception { - doTestSchedulerFactoryBeanWithExistingJobs(false); - } - - @Test - public void testSchedulerFactoryBeanWithOverwriteExistingJobs() throws Exception { - doTestSchedulerFactoryBeanWithExistingJobs(true); - } - - private void doTestSchedulerFactoryBeanWithExistingJobs(boolean overwrite) throws Exception { - TestBean tb = new TestBean("tb", 99); - JobDetailBean jobDetail0 = new JobDetailBean(); - jobDetail0.setJobClass(Job.class); - jobDetail0.setBeanName("myJob0"); - Map jobData = new HashMap(); - jobData.put("testBean", tb); - jobDetail0.setJobDataAsMap(jobData); - jobDetail0.afterPropertiesSet(); - assertEquals(tb, jobDetail0.getJobDataMap().get("testBean")); - - CronTriggerBean trigger0 = new CronTriggerBean(); - trigger0.setBeanName("myTrigger0"); - trigger0.setJobDetail(jobDetail0); - trigger0.setCronExpression("0/1 * * * * ?"); - trigger0.afterPropertiesSet(); - - TestMethodInvokingTask task1 = new TestMethodInvokingTask(); - MethodInvokingJobDetailFactoryBean mijdfb = new MethodInvokingJobDetailFactoryBean(); - mijdfb.setBeanName("myJob1"); - mijdfb.setTargetObject(task1); - mijdfb.setTargetMethod("doSomething"); - mijdfb.afterPropertiesSet(); - JobDetail jobDetail1 = mijdfb.getObject(); - - SimpleTriggerBean trigger1 = new SimpleTriggerBean(); - trigger1.setBeanName("myTrigger1"); - trigger1.setJobDetail(jobDetail1); - trigger1.setStartDelay(0); - trigger1.setRepeatInterval(20); - trigger1.afterPropertiesSet(); - - final Scheduler scheduler = mock(Scheduler.class); - given(scheduler.getContext()).willReturn(new SchedulerContext()); - given(scheduler.rescheduleJob("myTrigger1", Scheduler.DEFAULT_GROUP, trigger1)).willReturn(new Date()); - given(scheduler.getTrigger("myTrigger1", Scheduler.DEFAULT_GROUP)).willReturn(new SimpleTrigger()); - given(scheduler.scheduleJob(trigger0)).willReturn(new Date()); - - SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean() { - @Override - protected Scheduler createScheduler(SchedulerFactory schedulerFactory, String schedulerName) { - return scheduler; - } - }; - schedulerFactoryBean.setJobFactory(null); - Map schedulerContext = new HashMap(); - schedulerContext.put("otherTestBean", tb); - schedulerFactoryBean.setSchedulerContextAsMap(schedulerContext); - schedulerFactoryBean.setTriggers(new Trigger[] {trigger0, trigger1}); - if (overwrite) { - schedulerFactoryBean.setOverwriteExistingJobs(true); - } - try { - schedulerFactoryBean.afterPropertiesSet(); - schedulerFactoryBean.start(); - } - finally { - schedulerFactoryBean.destroy(); - } - - verify(scheduler).getTrigger("myTrigger0", Scheduler.DEFAULT_GROUP); - verify(scheduler).getTrigger("myTrigger1", Scheduler.DEFAULT_GROUP); - if (overwrite) { - verify(scheduler).addJob(jobDetail1, true); - verify(scheduler).rescheduleJob("myTrigger1", Scheduler.DEFAULT_GROUP, trigger1); - } - else { - verify(scheduler).getJobDetail("myJob0", Scheduler.DEFAULT_GROUP); - } - verify(scheduler).addJob(jobDetail0, true); - verify(scheduler).start(); - verify(scheduler).shutdown(false); - } - - @Test - public void testSchedulerFactoryBeanWithExistingJobsAndRaceCondition() throws Exception { - doTestSchedulerFactoryBeanWithExistingJobsAndRaceCondition(false); - } - - @Test - public void testSchedulerFactoryBeanWithOverwriteExistingJobsAndRaceCondition() throws Exception { - doTestSchedulerFactoryBeanWithExistingJobsAndRaceCondition(true); - } - - private void doTestSchedulerFactoryBeanWithExistingJobsAndRaceCondition(boolean overwrite) throws Exception { - TestBean tb = new TestBean("tb", 99); - JobDetailBean jobDetail0 = new JobDetailBean(); - jobDetail0.setJobClass(Job.class); - jobDetail0.setBeanName("myJob0"); - Map jobData = new HashMap(); - jobData.put("testBean", tb); - jobDetail0.setJobDataAsMap(jobData); - jobDetail0.afterPropertiesSet(); - assertEquals(tb, jobDetail0.getJobDataMap().get("testBean")); - - CronTriggerBean trigger0 = new CronTriggerBean(); - trigger0.setBeanName("myTrigger0"); - trigger0.setJobDetail(jobDetail0); - trigger0.setCronExpression("0/1 * * * * ?"); - trigger0.afterPropertiesSet(); - - TestMethodInvokingTask task1 = new TestMethodInvokingTask(); - MethodInvokingJobDetailFactoryBean mijdfb = new MethodInvokingJobDetailFactoryBean(); - mijdfb.setBeanName("myJob1"); - mijdfb.setTargetObject(task1); - mijdfb.setTargetMethod("doSomething"); - mijdfb.afterPropertiesSet(); - JobDetail jobDetail1 = mijdfb.getObject(); - - SimpleTriggerBean trigger1 = new SimpleTriggerBean(); - trigger1.setBeanName("myTrigger1"); - trigger1.setJobDetail(jobDetail1); - trigger1.setStartDelay(0); - trigger1.setRepeatInterval(20); - trigger1.afterPropertiesSet(); - - final Scheduler scheduler = mock(Scheduler.class); - given(scheduler.getContext()).willReturn(new SchedulerContext()); - given(scheduler.getTrigger("myTrigger1", Scheduler.DEFAULT_GROUP)).willReturn(new SimpleTrigger()); - if (overwrite) { - given(scheduler.rescheduleJob("myTrigger1", Scheduler.DEFAULT_GROUP, trigger1)).willReturn(new Date()); - } - given(scheduler.scheduleJob(trigger0)).willThrow(new ObjectAlreadyExistsException("")); - if (overwrite) { - given(scheduler.rescheduleJob("myTrigger0", Scheduler.DEFAULT_GROUP, trigger0)).willReturn(new Date()); - } - - SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean() { - @Override - protected Scheduler createScheduler(SchedulerFactory schedulerFactory, String schedulerName) { - return scheduler; - } - }; - schedulerFactoryBean.setJobFactory(null); - Map schedulerContext = new HashMap(); - schedulerContext.put("otherTestBean", tb); - schedulerFactoryBean.setSchedulerContextAsMap(schedulerContext); - schedulerFactoryBean.setTriggers(new Trigger[] {trigger0, trigger1}); - if (overwrite) { - schedulerFactoryBean.setOverwriteExistingJobs(true); - } - try { - schedulerFactoryBean.afterPropertiesSet(); - schedulerFactoryBean.start(); - } - finally { - schedulerFactoryBean.destroy(); - } - - verify(scheduler).getTrigger("myTrigger0", Scheduler.DEFAULT_GROUP); - verify(scheduler).getTrigger("myTrigger1", Scheduler.DEFAULT_GROUP); - if (overwrite) { - verify(scheduler).addJob(jobDetail1, true); - verify(scheduler).rescheduleJob("myTrigger1", Scheduler.DEFAULT_GROUP, trigger1); - } - else { - verify(scheduler).getJobDetail("myJob0", Scheduler.DEFAULT_GROUP); - } - verify(scheduler).addJob(jobDetail0, true); - verify(scheduler).start(); - verify(scheduler).shutdown(false); - } - - @Test - @SuppressWarnings("deprecation") - public void testSchedulerFactoryBeanWithListeners() throws Exception { - JobFactory jobFactory = new AdaptableJobFactory(); - - final Scheduler scheduler = mock(Scheduler.class); - - SchedulerListener schedulerListener = new TestSchedulerListener(); - JobListener globalJobListener = new TestJobListener(); - JobListener jobListener = new TestJobListener(); - TriggerListener globalTriggerListener = new TestTriggerListener(); - TriggerListener triggerListener = new TestTriggerListener(); - - SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean() { - @Override - protected Scheduler createScheduler(SchedulerFactory schedulerFactory, String schedulerName) { - return scheduler; - } - }; - schedulerFactoryBean.setJobFactory(jobFactory); - schedulerFactoryBean.setSchedulerListeners(new SchedulerListener[] {schedulerListener}); - schedulerFactoryBean.setGlobalJobListeners(new JobListener[] {globalJobListener}); - schedulerFactoryBean.setJobListeners(new JobListener[] {jobListener}); - schedulerFactoryBean.setGlobalTriggerListeners(new TriggerListener[] {globalTriggerListener}); - schedulerFactoryBean.setTriggerListeners(new TriggerListener[] {triggerListener}); - try { - schedulerFactoryBean.afterPropertiesSet(); - schedulerFactoryBean.start(); - } - finally { - schedulerFactoryBean.destroy(); - } - - verify(scheduler).setJobFactory(jobFactory); - verify(scheduler).addSchedulerListener(schedulerListener); - verify(scheduler).addGlobalJobListener(globalJobListener); - verify(scheduler).addJobListener(jobListener); - verify(scheduler).addGlobalTriggerListener(globalTriggerListener); - verify(scheduler).addTriggerListener(triggerListener); - verify(scheduler).start(); - verify(scheduler).shutdown(false); - } - - @Ignore @Test - public void testMethodInvocationWithConcurrency() throws Exception { - Assume.group(TestGroup.PERFORMANCE); - methodInvokingConcurrency(true); - } - - // We can't test both since Quartz somehow seems to keep things in memory - // enable both and one of them will fail (order doesn't matter). - @Ignore @Test - public void testMethodInvocationWithoutConcurrency() throws Exception { - Assume.group(TestGroup.PERFORMANCE); - methodInvokingConcurrency(false); - } - - private void methodInvokingConcurrency(boolean concurrent) throws Exception { - // Test the concurrency flag. - // Method invoking job with two triggers. - // If the concurrent flag is false, the triggers are NOT allowed - // to interfere with each other. - - TestMethodInvokingTask task1 = new TestMethodInvokingTask(); - MethodInvokingJobDetailFactoryBean mijdfb = new MethodInvokingJobDetailFactoryBean(); - // set the concurrency flag! - mijdfb.setConcurrent(concurrent); - mijdfb.setBeanName("myJob1"); - mijdfb.setTargetObject(task1); - mijdfb.setTargetMethod("doWait"); - mijdfb.afterPropertiesSet(); - JobDetail jobDetail1 = mijdfb.getObject(); - - SimpleTriggerBean trigger0 = new SimpleTriggerBean(); - trigger0.setBeanName("myTrigger1"); - trigger0.setJobDetail(jobDetail1); - trigger0.setStartDelay(0); - trigger0.setRepeatInterval(1); - trigger0.setRepeatCount(1); - trigger0.afterPropertiesSet(); - - SimpleTriggerBean trigger1 = new SimpleTriggerBean(); - trigger1.setBeanName("myTrigger1"); - trigger1.setJobDetail(jobDetail1); - trigger1.setStartDelay(1000L); - trigger1.setRepeatInterval(1); - trigger1.setRepeatCount(1); - trigger1.afterPropertiesSet(); - - SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); - schedulerFactoryBean.setJobDetails(new JobDetail[] {jobDetail1}); - schedulerFactoryBean.setTriggers(new Trigger[] {trigger1, trigger0}); - schedulerFactoryBean.afterPropertiesSet(); - - // ok scheduler is set up... let's wait for like 4 seconds - try { - Thread.sleep(4000); - } - catch (InterruptedException ex) { - // fall through - } - - if (concurrent) { - assertEquals(2, task1.counter); - task1.stop(); - // we're done, both jobs have ran, let's call it a day - return; - } - else { - assertEquals(1, task1.counter); - task1.stop(); - // we need to check whether or not the test succeed with non-concurrent jobs - } - - try { - Thread.sleep(4000); - } - catch (InterruptedException ex) { - // fall through - } - - task1.stop(); - assertEquals(2, task1.counter); - - // Although we're destroying the scheduler, it does seem to keep things in memory: - // When executing both tests (concurrent and non-concurrent), the second test always - // fails. - schedulerFactoryBean.destroy(); - } - - @Test - public void testSchedulerFactoryBeanWithPlainQuartzObjects() throws Exception { - JobFactory jobFactory = new AdaptableJobFactory(); - - TestBean tb = new TestBean("tb", 99); - JobDetail jobDetail0 = new JobDetail(); - jobDetail0.setJobClass(Job.class); - jobDetail0.setName("myJob0"); - jobDetail0.setGroup(Scheduler.DEFAULT_GROUP); - jobDetail0.getJobDataMap().put("testBean", tb); - assertEquals(tb, jobDetail0.getJobDataMap().get("testBean")); - - CronTrigger trigger0 = new CronTrigger(); - trigger0.setName("myTrigger0"); - trigger0.setGroup(Scheduler.DEFAULT_GROUP); - trigger0.setJobName("myJob0"); - trigger0.setJobGroup(Scheduler.DEFAULT_GROUP); - trigger0.setStartTime(new Date()); - trigger0.setCronExpression("0/1 * * * * ?"); - - TestMethodInvokingTask task1 = new TestMethodInvokingTask(); - MethodInvokingJobDetailFactoryBean mijdfb = new MethodInvokingJobDetailFactoryBean(); - mijdfb.setName("myJob1"); - mijdfb.setGroup(Scheduler.DEFAULT_GROUP); - mijdfb.setTargetObject(task1); - mijdfb.setTargetMethod("doSomething"); - mijdfb.afterPropertiesSet(); - JobDetail jobDetail1 = mijdfb.getObject(); - - SimpleTrigger trigger1 = new SimpleTrigger(); - trigger1.setName("myTrigger1"); - trigger1.setGroup(Scheduler.DEFAULT_GROUP); - trigger1.setJobName("myJob1"); - trigger1.setJobGroup(Scheduler.DEFAULT_GROUP); - trigger1.setStartTime(new Date()); - trigger1.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY); - trigger1.setRepeatInterval(20); - - final Scheduler scheduler = mock(Scheduler.class); - given(scheduler.scheduleJob(trigger0)).willReturn(new Date()); - given(scheduler.scheduleJob(trigger1)).willReturn(new Date()); - - SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean() { - @Override - protected Scheduler createScheduler(SchedulerFactory schedulerFactory, String schedulerName) { - return scheduler; - } - }; - schedulerFactoryBean.setJobFactory(jobFactory); - schedulerFactoryBean.setJobDetails(new JobDetail[] {jobDetail0, jobDetail1}); - schedulerFactoryBean.setTriggers(new Trigger[] {trigger0, trigger1}); - try { - schedulerFactoryBean.afterPropertiesSet(); - schedulerFactoryBean.start(); - } - finally { - schedulerFactoryBean.destroy(); - } - - verify(scheduler).setJobFactory(jobFactory); - verify(scheduler).getJobDetail("myJob0", Scheduler.DEFAULT_GROUP); - verify(scheduler).getJobDetail("myJob1", Scheduler.DEFAULT_GROUP); - verify(scheduler).getTrigger("myTrigger0", Scheduler.DEFAULT_GROUP); - verify(scheduler).getTrigger("myTrigger1", Scheduler.DEFAULT_GROUP); - verify(scheduler).addJob(jobDetail0, true); - verify(scheduler).addJob(jobDetail1, true); - verify(scheduler).scheduleJob(trigger0); - verify(scheduler).scheduleJob(trigger1); - verify(scheduler).start(); - verify(scheduler).shutdown(false); - } - @Test public void testSchedulerFactoryBeanWithApplicationContext() throws Exception { TestBean tb = new TestBean("tb", 99); @@ -571,68 +91,6 @@ public class QuartzSupportTests { verify(scheduler).shutdown(false); } - @Test - public void testJobDetailBeanWithApplicationContext() throws Exception { - TestBean tb = new TestBean("tb", 99); - StaticApplicationContext ac = new StaticApplicationContext(); - - JobDetailBean jobDetail = new JobDetailBean(); - jobDetail.setJobClass(Job.class); - jobDetail.setBeanName("myJob0"); - Map jobData = new HashMap(); - jobData.put("testBean", tb); - jobDetail.setJobDataAsMap(jobData); - jobDetail.setApplicationContext(ac); - jobDetail.setApplicationContextJobDataKey("appCtx"); - jobDetail.afterPropertiesSet(); - - assertEquals(tb, jobDetail.getJobDataMap().get("testBean")); - assertEquals(ac, jobDetail.getJobDataMap().get("appCtx")); - } - - @Test - @SuppressWarnings("deprecation") - public void testMethodInvokingJobDetailFactoryBeanWithListenerNames() throws Exception { - TestMethodInvokingTask task = new TestMethodInvokingTask(); - MethodInvokingJobDetailFactoryBean mijdfb = new MethodInvokingJobDetailFactoryBean(); - String[] names = new String[] {"test1", "test2"}; - mijdfb.setName("myJob1"); - mijdfb.setGroup(Scheduler.DEFAULT_GROUP); - mijdfb.setTargetObject(task); - mijdfb.setTargetMethod("doSomething"); - mijdfb.setJobListenerNames(names); - mijdfb.afterPropertiesSet(); - JobDetail jobDetail = mijdfb.getObject(); - assertArrayEquals(names, jobDetail.getJobListenerNames()); - } - - @Test - @SuppressWarnings("deprecation") - public void testJobDetailBeanWithListenerNames() { - JobDetailBean jobDetail = new JobDetailBean(); - String[] names = new String[] {"test1", "test2"}; - jobDetail.setJobListenerNames(names); - assertArrayEquals(names, jobDetail.getJobListenerNames()); - } - - @Test - @SuppressWarnings("deprecation") - public void testCronTriggerBeanWithListenerNames() { - CronTriggerBean trigger = new CronTriggerBean(); - String[] names = new String[] {"test1", "test2"}; - trigger.setTriggerListenerNames(names); - assertArrayEquals(names, trigger.getTriggerListenerNames()); - } - - @Test - @SuppressWarnings("deprecation") - public void testSimpleTriggerBeanWithListenerNames() { - SimpleTriggerBean trigger = new SimpleTriggerBean(); - String[] names = new String[] {"test1", "test2"}; - trigger.setTriggerListenerNames(names); - assertArrayEquals(names, trigger.getTriggerListenerNames()); - } - @Test public void testSchedulerWithTaskExecutor() throws Exception { Assume.group(TestGroup.PERFORMANCE); @@ -640,11 +98,11 @@ public class QuartzSupportTests { CountingTaskExecutor taskExecutor = new CountingTaskExecutor(); DummyJob.count = 0; - JobDetail jobDetail = new JobDetail(); + JobDetailImpl jobDetail = new JobDetailImpl(); jobDetail.setJobClass(DummyJob.class); jobDetail.setName("myJob"); - SimpleTriggerBean trigger = new SimpleTriggerBean(); + SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean(); trigger.setName("myTrigger"); trigger.setJobDetail(jobDetail); trigger.setStartDelay(1); @@ -654,8 +112,8 @@ public class QuartzSupportTests { SchedulerFactoryBean bean = new SchedulerFactoryBean(); bean.setTaskExecutor(taskExecutor); - bean.setTriggers(new Trigger[] {trigger}); - bean.setJobDetails(new JobDetail[] {jobDetail}); + bean.setTriggers(trigger.getObject()); + bean.setJobDetails(jobDetail); bean.afterPropertiesSet(); bean.start(); @@ -672,11 +130,11 @@ public class QuartzSupportTests { DummyRunnable.count = 0; - JobDetail jobDetail = new JobDetailBean(); - jobDetail.setJobClass(DummyRunnable.class); + JobDetailImpl jobDetail = new JobDetailImpl(); + jobDetail.setJobClass((Class) DummyRunnable.class); jobDetail.setName("myJob"); - SimpleTriggerBean trigger = new SimpleTriggerBean(); + SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean(); trigger.setName("myTrigger"); trigger.setJobDetail(jobDetail); trigger.setStartDelay(1); @@ -685,8 +143,8 @@ public class QuartzSupportTests { trigger.afterPropertiesSet(); SchedulerFactoryBean bean = new SchedulerFactoryBean(); - bean.setTriggers(new Trigger[] {trigger}); - bean.setJobDetails(new JobDetail[] {jobDetail}); + bean.setTriggers(trigger.getObject()); + bean.setJobDetails(jobDetail); bean.afterPropertiesSet(); bean.start(); @@ -703,12 +161,12 @@ public class QuartzSupportTests { DummyJob.param = 0; DummyJob.count = 0; - JobDetail jobDetail = new JobDetail(); + JobDetailImpl jobDetail = new JobDetailImpl(); jobDetail.setJobClass(DummyJobBean.class); jobDetail.setName("myJob"); jobDetail.getJobDataMap().put("param", "10"); - SimpleTriggerBean trigger = new SimpleTriggerBean(); + SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean(); trigger.setName("myTrigger"); trigger.setJobDetail(jobDetail); trigger.setStartDelay(1); @@ -717,8 +175,8 @@ public class QuartzSupportTests { trigger.afterPropertiesSet(); SchedulerFactoryBean bean = new SchedulerFactoryBean(); - bean.setTriggers(new Trigger[] {trigger}); - bean.setJobDetails(new JobDetail[] {jobDetail}); + bean.setTriggers(trigger.getObject()); + bean.setJobDetails(jobDetail); bean.afterPropertiesSet(); bean.start(); @@ -736,13 +194,13 @@ public class QuartzSupportTests { DummyJob.param = 0; DummyJob.count = 0; - JobDetail jobDetail = new JobDetail(); + JobDetailImpl jobDetail = new JobDetailImpl(); jobDetail.setJobClass(DummyJob.class); jobDetail.setName("myJob"); jobDetail.getJobDataMap().put("param", "10"); jobDetail.getJobDataMap().put("ignoredParam", "10"); - SimpleTriggerBean trigger = new SimpleTriggerBean(); + SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean(); trigger.setName("myTrigger"); trigger.setJobDetail(jobDetail); trigger.setStartDelay(1); @@ -752,8 +210,8 @@ public class QuartzSupportTests { SchedulerFactoryBean bean = new SchedulerFactoryBean(); bean.setJobFactory(new SpringBeanJobFactory()); - bean.setTriggers(new Trigger[] {trigger}); - bean.setJobDetails(new JobDetail[] {jobDetail}); + bean.setTriggers(trigger.getObject()); + bean.setJobDetails(jobDetail); bean.afterPropertiesSet(); bean.start(); @@ -767,16 +225,17 @@ public class QuartzSupportTests { @Test public void testSchedulerWithSpringBeanJobFactoryAndParamMismatchNotIgnored() throws Exception { Assume.group(TestGroup.PERFORMANCE); + DummyJob.param = 0; DummyJob.count = 0; - JobDetail jobDetail = new JobDetail(); + JobDetailImpl jobDetail = new JobDetailImpl(); jobDetail.setJobClass(DummyJob.class); jobDetail.setName("myJob"); jobDetail.getJobDataMap().put("para", "10"); jobDetail.getJobDataMap().put("ignoredParam", "10"); - SimpleTriggerBean trigger = new SimpleTriggerBean(); + SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean(); trigger.setName("myTrigger"); trigger.setJobDetail(jobDetail); trigger.setStartDelay(1); @@ -786,10 +245,10 @@ public class QuartzSupportTests { SchedulerFactoryBean bean = new SchedulerFactoryBean(); SpringBeanJobFactory jobFactory = new SpringBeanJobFactory(); - jobFactory.setIgnoredUnknownProperties(new String[] {"ignoredParam"}); + jobFactory.setIgnoredUnknownProperties("ignoredParam"); bean.setJobFactory(jobFactory); - bean.setTriggers(new Trigger[] {trigger}); - bean.setJobDetails(new JobDetail[] {jobDetail}); + bean.setTriggers(trigger.getObject()); + bean.setJobDetails(jobDetail); bean.afterPropertiesSet(); Thread.sleep(500); @@ -806,12 +265,12 @@ public class QuartzSupportTests { DummyRunnable.param = 0; DummyRunnable.count = 0; - JobDetail jobDetail = new JobDetailBean(); - jobDetail.setJobClass(DummyRunnable.class); + JobDetailImpl jobDetail = new JobDetailImpl(); + jobDetail.setJobClass((Class) DummyRunnable.class); jobDetail.setName("myJob"); jobDetail.getJobDataMap().put("param", "10"); - SimpleTriggerBean trigger = new SimpleTriggerBean(); + SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean(); trigger.setName("myTrigger"); trigger.setJobDetail(jobDetail); trigger.setStartDelay(1); @@ -821,8 +280,8 @@ public class QuartzSupportTests { SchedulerFactoryBean bean = new SchedulerFactoryBean(); bean.setJobFactory(new SpringBeanJobFactory()); - bean.setTriggers(new Trigger[] {trigger}); - bean.setJobDetails(new JobDetail[] {jobDetail}); + bean.setTriggers(trigger.getObject()); + bean.setJobDetails(jobDetail); bean.afterPropertiesSet(); bean.start(); @@ -839,12 +298,12 @@ public class QuartzSupportTests { DummyJobBean.param = 0; DummyJobBean.count = 0; - JobDetail jobDetail = new JobDetail(); + JobDetailImpl jobDetail = new JobDetailImpl(); jobDetail.setJobClass(DummyJobBean.class); jobDetail.setName("myJob"); jobDetail.getJobDataMap().put("param", "10"); - SimpleTriggerBean trigger = new SimpleTriggerBean(); + SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean(); trigger.setName("myTrigger"); trigger.setJobDetail(jobDetail); trigger.setStartDelay(1); @@ -854,8 +313,8 @@ public class QuartzSupportTests { SchedulerFactoryBean bean = new SchedulerFactoryBean(); bean.setJobFactory(new SpringBeanJobFactory()); - bean.setTriggers(new Trigger[] {trigger}); - bean.setJobDetails(new JobDetail[] {jobDetail}); + bean.setTriggers(trigger.getObject()); + bean.setJobDetails(jobDetail); bean.afterPropertiesSet(); bean.start(); @@ -992,115 +451,9 @@ public class QuartzSupportTests { try { // assertEquals(10, DummyJob.param); assertTrue(DummyJob.count > 0); - } finally { - ctx.close(); - } - - } - - private static class TestSchedulerListener implements SchedulerListener { - - @Override - public void jobScheduled(Trigger trigger) { - } - - @Override - public void jobUnscheduled(String triggerName, String triggerGroup) { - } - - @Override - public void triggerFinalized(Trigger trigger) { } - - @Override - public void triggersPaused(String triggerName, String triggerGroup) { - } - - @Override - public void triggersResumed(String triggerName, String triggerGroup) { - } - - @Override - public void jobsPaused(String jobName, String jobGroup) { - } - - @Override - public void jobsResumed(String jobName, String jobGroup) { - } - - @Override - public void schedulerError(String msg, SchedulerException cause) { - } - - @Override - public void schedulerShutdown() { - } - - @Override - public void jobAdded(JobDetail jobDetail) { - } - - @Override - public void jobDeleted(String s, String s1) { - } - - @Override - public void schedulerInStandbyMode() { - } - - @Override - public void schedulerStarted() { - } - - @Override - public void schedulerShuttingdown() { - } - } - - - private static class TestJobListener implements JobListener { - - @Override - public String getName() { - return null; - } - - @Override - public void jobToBeExecuted(JobExecutionContext context) { - } - - @Override - public void jobExecutionVetoed(JobExecutionContext context) { - } - - @Override - public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { - } - } - - - private static class TestTriggerListener implements TriggerListener { - - @Override - public String getName() { - return null; - } - - @Override - public void triggerFired(Trigger trigger, JobExecutionContext context) { - } - - @Override - public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) { - return false; - } - - @Override - public void triggerMisfired(Trigger trigger) { - } - - @Override - public void triggerComplete(Trigger trigger, JobExecutionContext context, int triggerInstructionCode) { + finally { + ctx.close(); } } diff --git a/spring-context-support/src/test/resources/org/springframework/scheduling/quartz/databasePersistence.xml b/spring-context-support/src/test/resources/org/springframework/scheduling/quartz/databasePersistence.xml index f3fe1a38a27..2048482bf8b 100644 --- a/spring-context-support/src/test/resources/org/springframework/scheduling/quartz/databasePersistence.xml +++ b/spring-context-support/src/test/resources/org/springframework/scheduling/quartz/databasePersistence.xml @@ -10,11 +10,11 @@ - + - + diff --git a/spring-context-support/src/test/resources/org/springframework/scheduling/quartz/multipleAnonymousMethodInvokingJobDetailFB.xml b/spring-context-support/src/test/resources/org/springframework/scheduling/quartz/multipleAnonymousMethodInvokingJobDetailFB.xml index 40f5a8d7626..ae2ec8bd34d 100644 --- a/spring-context-support/src/test/resources/org/springframework/scheduling/quartz/multipleAnonymousMethodInvokingJobDetailFB.xml +++ b/spring-context-support/src/test/resources/org/springframework/scheduling/quartz/multipleAnonymousMethodInvokingJobDetailFB.xml @@ -12,7 +12,7 @@ - + @@ -23,7 +23,7 @@ - + diff --git a/spring-context-support/src/test/resources/org/springframework/scheduling/quartz/schedulerAccessorBean.xml b/spring-context-support/src/test/resources/org/springframework/scheduling/quartz/schedulerAccessorBean.xml index 0c4c9578b2d..a7b1bf8cb12 100644 --- a/spring-context-support/src/test/resources/org/springframework/scheduling/quartz/schedulerAccessorBean.xml +++ b/spring-context-support/src/test/resources/org/springframework/scheduling/quartz/schedulerAccessorBean.xml @@ -15,7 +15,7 @@ - + @@ -26,7 +26,7 @@ - + diff --git a/spring-context-support/src/test/resources/org/springframework/scheduling/quartz/schedulerRepositoryExposure.xml b/spring-context-support/src/test/resources/org/springframework/scheduling/quartz/schedulerRepositoryExposure.xml index 46410a7cd31..14fd5725c25 100644 --- a/spring-context-support/src/test/resources/org/springframework/scheduling/quartz/schedulerRepositoryExposure.xml +++ b/spring-context-support/src/test/resources/org/springframework/scheduling/quartz/schedulerRepositoryExposure.xml @@ -18,7 +18,7 @@ - + @@ -29,7 +29,7 @@ - + diff --git a/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJacksonMessageConverter.java b/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJacksonMessageConverter.java deleted file mode 100644 index 1bcec4465b7..00000000000 --- a/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJacksonMessageConverter.java +++ /dev/null @@ -1,383 +0,0 @@ -/* - * Copyright 2002-2014 the original author or authors. - * - * Licensed 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.springframework.jms.support.converter; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.StringWriter; -import java.io.UnsupportedEncodingException; -import java.util.HashMap; -import java.util.Map; -import javax.jms.BytesMessage; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.Session; -import javax.jms.TextMessage; - -import org.codehaus.jackson.map.ObjectMapper; -import org.codehaus.jackson.type.JavaType; - -import org.springframework.beans.factory.BeanClassLoaderAware; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; - -/** - * Message converter that uses Jackson 1.x to convert messages to and from JSON. - * Maps an object to a {@link BytesMessage}, or to a {@link TextMessage} if the - * {@link #setTargetType targetType} is set to {@link MessageType#TEXT}. - * Converts from a {@link TextMessage} or {@link BytesMessage} to an object. - * - *

NOTE: Requires Jackson 1.8 or higher, as of Spring 4.0. - * At the same time, we strongly recommend a migration to Jackson 2.x! - * - * @author Mark Pollack - * @author Dave Syer - * @author Juergen Hoeller - * @since 3.1 - * @deprecated Please migrate to {@link MappingJackson2MessageConverter} for Jackson 2.x. - */ -@Deprecated -public class MappingJacksonMessageConverter implements MessageConverter, BeanClassLoaderAware { - - /** - * The default encoding used for writing to text messages: UTF-8. - */ - public static final String DEFAULT_ENCODING = "UTF-8"; - - - private ObjectMapper objectMapper = new ObjectMapper(); - - private MessageType targetType = MessageType.BYTES; - - private String encoding = DEFAULT_ENCODING; - - private String encodingPropertyName; - - private String typeIdPropertyName; - - private Map> idClassMappings = new HashMap>(); - - private Map, String> classIdMappings = new HashMap, String>(); - - private ClassLoader beanClassLoader; - - - /** - * Specify the {@link ObjectMapper} to use instead of using the default. - */ - public void setObjectMapper(ObjectMapper objectMapper) { - Assert.notNull(objectMapper, "ObjectMapper must not be null"); - this.objectMapper = objectMapper; - } - - /** - * Specify whether {@link #toMessage(Object, Session)} should marshal to a - * {@link BytesMessage} or a {@link TextMessage}. - *

The default is {@link MessageType#BYTES}, i.e. this converter marshals to - * a {@link BytesMessage}. Note that the default version of this converter - * supports {@link MessageType#BYTES} and {@link MessageType#TEXT} only. - * @see MessageType#BYTES - * @see MessageType#TEXT - */ - public void setTargetType(MessageType targetType) { - Assert.notNull(targetType, "MessageType must not be null"); - this.targetType = targetType; - } - - /** - * Specify the encoding to use when converting to and from text-based - * message body content. The default encoding will be "UTF-8". - *

When reading from a a text-based message, an encoding may have been - * suggested through a special JMS property which will then be preferred - * over the encoding set on this MessageConverter instance. - * @see #setEncodingPropertyName - */ - public void setEncoding(String encoding) { - this.encoding = encoding; - } - - /** - * Specify the name of the JMS message property that carries the encoding from - * bytes to String and back is BytesMessage is used during the conversion process. - *

Default is none. Setting this property is optional; if not set, UTF-8 will - * be used for decoding any incoming bytes message. - * @see #setEncoding - */ - public void setEncodingPropertyName(String encodingPropertyName) { - this.encodingPropertyName = encodingPropertyName; - } - - /** - * Specify the name of the JMS message property that carries the type id for the - * contained object: either a mapped id value or a raw Java class name. - *

Default is none. NOTE: This property needs to be set in order to allow - * for converting from an incoming message to a Java object. - * @see #setTypeIdMappings - */ - public void setTypeIdPropertyName(String typeIdPropertyName) { - this.typeIdPropertyName = typeIdPropertyName; - } - - /** - * Specify mappings from type ids to Java classes, if desired. - * This allows for synthetic ids in the type id message property, - * instead of transferring Java class names. - *

Default is no custom mappings, i.e. transferring raw Java class names. - * @param typeIdMappings a Map with type id values as keys and Java classes as values - */ - public void setTypeIdMappings(Map> typeIdMappings) { - this.idClassMappings = new HashMap>(); - for (Map.Entry> entry : typeIdMappings.entrySet()) { - String id = entry.getKey(); - Class clazz = entry.getValue(); - this.idClassMappings.put(id, clazz); - this.classIdMappings.put(clazz, id); - } - } - - @Override - public void setBeanClassLoader(ClassLoader classLoader) { - this.beanClassLoader = classLoader; - } - - - @Override - public Message toMessage(Object object, Session session) throws JMSException, MessageConversionException { - Message message; - try { - switch (this.targetType) { - case TEXT: - message = mapToTextMessage(object, session, this.objectMapper); - break; - case BYTES: - message = mapToBytesMessage(object, session, this.objectMapper); - break; - default: - message = mapToMessage(object, session, this.objectMapper, this.targetType); - } - } - catch (IOException ex) { - throw new MessageConversionException("Could not map JSON object [" + object + "]", ex); - } - setTypeIdOnMessage(object, message); - return message; - } - - @Override - public Object fromMessage(Message message) throws JMSException, MessageConversionException { - try { - JavaType targetJavaType = getJavaTypeForMessage(message); - return convertToObject(message, targetJavaType); - } - catch (IOException ex) { - throw new MessageConversionException("Failed to convert JSON message content", ex); - } - } - - - /** - * Map the given object to a {@link TextMessage}. - * @param object the object to be mapped - * @param session current JMS session - * @param objectMapper the mapper to use - * @return the resulting message - * @throws JMSException if thrown by JMS methods - * @throws IOException in case of I/O errors - * @see Session#createBytesMessage - */ - protected TextMessage mapToTextMessage(Object object, Session session, ObjectMapper objectMapper) - throws JMSException, IOException { - - StringWriter writer = new StringWriter(); - objectMapper.writeValue(writer, object); - return session.createTextMessage(writer.toString()); - } - - /** - * Map the given object to a {@link BytesMessage}. - * @param object the object to be mapped - * @param session current JMS session - * @param objectMapper the mapper to use - * @return the resulting message - * @throws JMSException if thrown by JMS methods - * @throws IOException in case of I/O errors - * @see Session#createBytesMessage - */ - protected BytesMessage mapToBytesMessage(Object object, Session session, ObjectMapper objectMapper) - throws JMSException, IOException { - - ByteArrayOutputStream bos = new ByteArrayOutputStream(1024); - OutputStreamWriter writer = new OutputStreamWriter(bos, this.encoding); - objectMapper.writeValue(writer, object); - - BytesMessage message = session.createBytesMessage(); - message.writeBytes(bos.toByteArray()); - if (this.encodingPropertyName != null) { - message.setStringProperty(this.encodingPropertyName, this.encoding); - } - return message; - } - - /** - * Template method that allows for custom message mapping. - * Invoked when {@link #setTargetType} is not {@link MessageType#TEXT} or - * {@link MessageType#BYTES}. - *

The default implementation throws an {@link IllegalArgumentException}. - * @param object the object to marshal - * @param session the JMS Session - * @param objectMapper the mapper to use - * @param targetType the target message type (other than TEXT or BYTES) - * @return the resulting message - * @throws JMSException if thrown by JMS methods - * @throws IOException in case of I/O errors - */ - protected Message mapToMessage(Object object, Session session, ObjectMapper objectMapper, MessageType targetType) - throws JMSException, IOException { - - throw new IllegalArgumentException("Unsupported message type [" + targetType + - "]. MappingJacksonMessageConverter by default only supports TextMessages and BytesMessages."); - } - - /** - * Set a type id for the given payload object on the given JMS Message. - *

The default implementation consults the configured type id mapping and - * sets the resulting value (either a mapped id or the raw Java class name) - * into the configured type id message property. - * @param object the payload object to set a type id for - * @param message the JMS Message to set the type id on - * @throws JMSException if thrown by JMS methods - * @see #getJavaTypeForMessage(javax.jms.Message) - * @see #setTypeIdPropertyName(String) - * @see #setTypeIdMappings(java.util.Map) - */ - protected void setTypeIdOnMessage(Object object, Message message) throws JMSException { - if (this.typeIdPropertyName != null) { - String typeId = this.classIdMappings.get(object.getClass()); - if (typeId == null) { - typeId = object.getClass().getName(); - } - message.setStringProperty(this.typeIdPropertyName, typeId); - } - } - - - /** - * Convenience method to dispatch to converters for individual message types. - */ - private Object convertToObject(Message message, JavaType targetJavaType) throws JMSException, IOException { - if (message instanceof TextMessage) { - return convertFromTextMessage((TextMessage) message, targetJavaType); - } - else if (message instanceof BytesMessage) { - return convertFromBytesMessage((BytesMessage) message, targetJavaType); - } - else { - return convertFromMessage(message, targetJavaType); - } - } - - /** - * Convert a TextMessage to a Java Object with the specified type. - * @param message the input message - * @param targetJavaType the target type - * @return the message converted to an object - * @throws JMSException if thrown by JMS - * @throws IOException in case of I/O errors - */ - protected Object convertFromTextMessage(TextMessage message, JavaType targetJavaType) - throws JMSException, IOException { - - String body = message.getText(); - return this.objectMapper.readValue(body, targetJavaType); - } - - /** - * Convert a BytesMessage to a Java Object with the specified type. - * @param message the input message - * @param targetJavaType the target type - * @return the message converted to an object - * @throws JMSException if thrown by JMS - * @throws IOException in case of I/O errors - */ - protected Object convertFromBytesMessage(BytesMessage message, JavaType targetJavaType) - throws JMSException, IOException { - - String encoding = this.encoding; - if (this.encodingPropertyName != null && message.propertyExists(this.encodingPropertyName)) { - encoding = message.getStringProperty(this.encodingPropertyName); - } - byte[] bytes = new byte[(int) message.getBodyLength()]; - message.readBytes(bytes); - try { - String body = new String(bytes, encoding); - return this.objectMapper.readValue(body, targetJavaType); - } - catch (UnsupportedEncodingException ex) { - throw new MessageConversionException("Cannot convert bytes to String", ex); - } - } - - /** - * Template method that allows for custom message mapping. - * Invoked when {@link #setTargetType} is not {@link MessageType#TEXT} or - * {@link MessageType#BYTES}. - *

The default implementation throws an {@link IllegalArgumentException}. - * @param message the input message - * @param targetJavaType the target type - * @return the message converted to an object - * @throws JMSException if thrown by JMS - * @throws IOException in case of I/O errors - */ - protected Object convertFromMessage(Message message, JavaType targetJavaType) - throws JMSException, IOException { - - throw new IllegalArgumentException("Unsupported message type [" + message.getClass() + - "]. MappingJacksonMessageConverter by default only supports TextMessages and BytesMessages."); - } - - /** - * Determine a Jackson JavaType for the given JMS Message, - * typically parsing a type id message property. - *

The default implementation parses the configured type id property name - * and consults the configured type id mapping. This can be overridden with - * a different strategy, e.g. doing some heuristics based on message origin. - * @param message the JMS Message to set the type id on - * @throws JMSException if thrown by JMS methods - * @see #setTypeIdOnMessage(Object, javax.jms.Message) - * @see #setTypeIdPropertyName(String) - * @see #setTypeIdMappings(java.util.Map) - */ - protected JavaType getJavaTypeForMessage(Message message) throws JMSException { - String typeId = message.getStringProperty(this.typeIdPropertyName); - if (typeId == null) { - throw new MessageConversionException("Could not find type id property [" + this.typeIdPropertyName + "]"); - } - Class mappedClass = this.idClassMappings.get(typeId); - if (mappedClass != null) { - return this.objectMapper.getTypeFactory().constructType(mappedClass); - } - try { - Class typeClass = ClassUtils.forName(typeId, this.beanClassLoader); - return this.objectMapper.getTypeFactory().constructType(typeClass); - } - catch (Throwable ex) { - throw new MessageConversionException("Failed to resolve type id [" + typeId + "]", ex); - } - } - -} diff --git a/spring-jms/src/test/java/org/springframework/jms/support/converter/MappingJacksonMessageConverterTests.java b/spring-jms/src/test/java/org/springframework/jms/support/converter/MappingJacksonMessageConverterTests.java deleted file mode 100644 index c38acef0555..00000000000 --- a/spring-jms/src/test/java/org/springframework/jms/support/converter/MappingJacksonMessageConverterTests.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2002-2014 the original author or authors. - * - * Licensed 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.springframework.jms.support.converter; - -import java.io.ByteArrayInputStream; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -import javax.jms.BytesMessage; -import javax.jms.Session; -import javax.jms.TextMessage; - -import org.junit.Before; -import org.junit.Test; - -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import static org.junit.Assert.*; -import static org.mockito.BDDMockito.*; - -/** - * @author Arjen Poutsma - * @author Dave Syer - */ -@SuppressWarnings("deprecation") -public class MappingJacksonMessageConverterTests { - - private MappingJacksonMessageConverter converter; - - private Session sessionMock; - - - @Before - public void setUp() throws Exception { - sessionMock = mock(Session.class); - converter = new MappingJacksonMessageConverter(); - converter.setEncodingPropertyName("__encoding__"); - converter.setTypeIdPropertyName("__typeid__"); - } - - - @Test - public void toBytesMessage() throws Exception { - BytesMessage bytesMessageMock = mock(BytesMessage.class); - Date toBeMarshalled = new Date(); - - given(sessionMock.createBytesMessage()).willReturn(bytesMessageMock); - - converter.toMessage(toBeMarshalled, sessionMock); - - verify(bytesMessageMock).setStringProperty("__encoding__", "UTF-8"); - verify(bytesMessageMock).setStringProperty("__typeid__", Date.class.getName()); - verify(bytesMessageMock).writeBytes(isA(byte[].class)); - } - - @Test - public void fromBytesMessage() throws Exception { - BytesMessage bytesMessageMock = mock(BytesMessage.class); - Map unmarshalled = Collections.singletonMap("foo", "bar"); - - byte[] bytes = "{\"foo\":\"bar\"}".getBytes(); - final ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes); - - given(bytesMessageMock.getStringProperty("__typeid__")).willReturn(Object.class.getName()); - given(bytesMessageMock.propertyExists("__encoding__")).willReturn(false); - given(bytesMessageMock.getBodyLength()).willReturn(new Long(bytes.length)); - given(bytesMessageMock.readBytes(any(byte[].class))).willAnswer( - new Answer() { - @Override - public Integer answer(InvocationOnMock invocation) throws Throwable { - return byteStream.read((byte[]) invocation.getArguments()[0]); - } - }); - - Object result = converter.fromMessage(bytesMessageMock); - assertEquals("Invalid result", result, unmarshalled); - } - - @Test - public void toTextMessageWithObject() throws Exception { - converter.setTargetType(MessageType.TEXT); - TextMessage textMessageMock = mock(TextMessage.class); - Date toBeMarshalled = new Date(); - - given(sessionMock.createTextMessage(isA(String.class))).willReturn(textMessageMock); - - converter.toMessage(toBeMarshalled, sessionMock); - verify(textMessageMock).setStringProperty("__typeid__", Date.class.getName()); - } - - @Test - public void toTextMessageWithMap() throws Exception { - converter.setTargetType(MessageType.TEXT); - TextMessage textMessageMock = mock(TextMessage.class); - Map toBeMarshalled = new HashMap(); - toBeMarshalled.put("foo", "bar"); - - given(sessionMock.createTextMessage(isA(String.class))).willReturn(textMessageMock); - - converter.toMessage(toBeMarshalled, sessionMock); - verify(textMessageMock).setStringProperty("__typeid__", HashMap.class.getName()); - } - - @Test - public void fromTextMessageAsObject() throws Exception { - TextMessage textMessageMock = mock(TextMessage.class); - Map unmarshalled = Collections.singletonMap("foo", "bar"); - - String text = "{\"foo\":\"bar\"}"; - given(textMessageMock.getStringProperty("__typeid__")).willReturn(Object.class.getName()); - given(textMessageMock.getText()).willReturn(text); - - Object result = converter.fromMessage(textMessageMock); - assertEquals("Invalid result", result, unmarshalled); - } - - @Test - public void fromTextMessageAsMap() throws Exception { - TextMessage textMessageMock = mock(TextMessage.class); - Map unmarshalled = Collections.singletonMap("foo", "bar"); - - String text = "{\"foo\":\"bar\"}"; - given(textMessageMock.getStringProperty("__typeid__")).willReturn(HashMap.class.getName()); - given(textMessageMock.getText()).willReturn(text); - - Object result = converter.fromMessage(textMessageMock); - assertEquals("Invalid result", result, unmarshalled); - } - -} diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/JacksonObjectMapperFactoryBean.java b/spring-web/src/main/java/org/springframework/http/converter/json/JacksonObjectMapperFactoryBean.java deleted file mode 100644 index 299ceadddec..00000000000 --- a/spring-web/src/main/java/org/springframework/http/converter/json/JacksonObjectMapperFactoryBean.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * Licensed 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.springframework.http.converter.json; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.HashMap; -import java.util.Map; - -import org.codehaus.jackson.JsonGenerator; -import org.codehaus.jackson.JsonParser; -import org.codehaus.jackson.map.AnnotationIntrospector; -import org.codehaus.jackson.map.DeserializationConfig; -import org.codehaus.jackson.map.ObjectMapper; -import org.codehaus.jackson.map.SerializationConfig; - -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.InitializingBean; - -/** - * A {@link FactoryBean} for creating a Jackson 1.x {@link ObjectMapper} with setters - * to enable or disable Jackson features from within XML configuration. - * - *

Example usage with MappingJacksonHttpMessageConverter: - *

- * <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
- * 	<property name="objectMapper">
- * 		<bean class="org.springframework.web.context.support.JacksonObjectMapperFactoryBean"
- * 			p:autoDetectFields="false"
- * 			p:autoDetectGettersSetters="false"
- * 			p:annotationIntrospector-ref="jaxbAnnotationIntrospector" />
- * 	</property>
- * </bean>
- * 
- * - *

Example usage with MappingJacksonJsonView: - *

- * <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
- * 	<property name="objectMapper">
- * 		<bean class="org.springframework.web.context.support.JacksonObjectMapperFactoryBean"
- * 			p:autoDetectFields="false"
- * 			p:autoDetectGettersSetters="false"
- * 			p:annotationIntrospector-ref="jaxbAnnotationIntrospector" />
- * 	</property>
- * </bean>
- * 
- * - *

In case there are no specific setters provided (for some rarely used - * options), you can still use the more general methods - * {@link #setFeaturesToEnable(Object[])} and {@link #setFeaturesToDisable(Object[])}. - * - *

- * <bean class="org.springframework.web.context.support.JacksonObjectMapperFactoryBean">
- * 	<property name="featuresToEnable">
- * 		<array>
- * 			<util:constant static-field="org.codehaus.jackson.map.SerializationConfig$Feature.WRAP_ROOT_VALUE"/>
- * 			<util:constant static-field="org.codehaus.jackson.map.SerializationConfig$Feature.CLOSE_CLOSEABLE"/>
- * 		</array>
- * 	</property>
- * 	<property name="featuresToDisable">
- * 		<array>
- * 			<util:constant static-field="org.codehaus.jackson.map.DeserializationConfig$Feature.USE_ANNOTATIONS"/>
- * 		</array>
- * 	</property>
- * </bean>
- * 
- * - *

NOTE: Requires Jackson 1.8 or higher, as of Spring 4.0. - * At the same time, we strongly recommend a migration to Jackson 2.x! - * - * @author Dmitry Katsubo - * @author Rossen Stoyanchev - * @since 3.2 - * @deprecated Please migrate to {@link Jackson2ObjectMapperFactoryBean} for Jackson 2.x. - */ -@Deprecated -public class JacksonObjectMapperFactoryBean implements FactoryBean, InitializingBean { - - private ObjectMapper objectMapper; - - private Map features = new HashMap(); - - private DateFormat dateFormat; - - private AnnotationIntrospector annotationIntrospector; - - - /** - * Set the ObjectMapper instance to use. - * If not set an instance will be created using the default constructor. - */ - public void setObjectMapper(ObjectMapper objectMapper) { - this.objectMapper = objectMapper; - } - - /** - * Define the format for date/time with the given {@link DateFormat}. - *

Note: Setting this property makes the exposed {@link ObjectMapper} - * non-thread-safe, according to Jackson's thread safety rules. - * @see #setSimpleDateFormat(String) - */ - public void setDateFormat(DateFormat dateFormat) { - this.dateFormat = dateFormat; - } - - /** - * Define the date/time format with a {@link SimpleDateFormat}. - *

Note: Setting this property makes the exposed {@link ObjectMapper} - * non-thread-safe, according to Jackson's thread safety rules. - * @see #setDateFormat(DateFormat) - */ - public void setSimpleDateFormat(String format) { - this.dateFormat = new SimpleDateFormat(format); - } - - /** - * Set the {@link AnnotationIntrospector} for serialization and deserialization. - * @see SerializationConfig#setAnnotationIntrospector(AnnotationIntrospector) - * @see DeserializationConfig#setAnnotationIntrospector(AnnotationIntrospector) - */ - public void setAnnotationIntrospector(AnnotationIntrospector annotationIntrospector) { - this.annotationIntrospector = annotationIntrospector; - } - - /** - * Shortcut for {@link org.codehaus.jackson.map.SerializationConfig.Feature#AUTO_DETECT_FIELDS} and - * {@link org.codehaus.jackson.map.DeserializationConfig.Feature#AUTO_DETECT_FIELDS}. - */ - public void setAutoDetectFields(boolean autoDetectFields) { - this.features.put(SerializationConfig.Feature.AUTO_DETECT_FIELDS, autoDetectFields); - this.features.put(DeserializationConfig.Feature.AUTO_DETECT_FIELDS, autoDetectFields); - } - - /** - * Shortcut for {@link org.codehaus.jackson.map.SerializationConfig.Feature#AUTO_DETECT_GETTERS} and - * {@link org.codehaus.jackson.map.DeserializationConfig.Feature#AUTO_DETECT_SETTERS}. - */ - public void setAutoDetectGettersSetters(boolean autoDetectGettersSetters) { - this.features.put(SerializationConfig.Feature.AUTO_DETECT_GETTERS, autoDetectGettersSetters); - this.features.put(DeserializationConfig.Feature.AUTO_DETECT_SETTERS, autoDetectGettersSetters); - } - - /** - * Shortcut for {@link org.codehaus.jackson.map.SerializationConfig.Feature#FAIL_ON_EMPTY_BEANS}. - */ - public void setFailOnEmptyBeans(boolean failOnEmptyBeans) { - this.features.put(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, failOnEmptyBeans); - } - - /** - * Shortcut for {@link org.codehaus.jackson.map.SerializationConfig.Feature#INDENT_OUTPUT}. - */ - public void setIndentOutput(boolean indentOutput) { - this.features.put(SerializationConfig.Feature.INDENT_OUTPUT, indentOutput); - } - - /** - * Specify features to enable. - * @see org.codehaus.jackson.JsonParser.Feature - * @see org.codehaus.jackson.JsonGenerator.Feature - * @see org.codehaus.jackson.map.SerializationConfig.Feature - * @see org.codehaus.jackson.map.DeserializationConfig.Feature - */ - public void setFeaturesToEnable(Object[] featuresToEnable) { - if (featuresToEnable != null) { - for (Object feature : featuresToEnable) { - this.features.put(feature, Boolean.TRUE); - } - } - } - - /** - * Specify features to disable. - * @see org.codehaus.jackson.JsonParser.Feature - * @see org.codehaus.jackson.JsonGenerator.Feature - * @see org.codehaus.jackson.map.SerializationConfig.Feature - * @see org.codehaus.jackson.map.DeserializationConfig.Feature - */ - public void setFeaturesToDisable(Object[] featuresToDisable) { - if (featuresToDisable != null) { - for (Object feature : featuresToDisable) { - this.features.put(feature, Boolean.FALSE); - } - } - } - - - @Override - public void afterPropertiesSet() { - if (this.objectMapper == null) { - this.objectMapper = new ObjectMapper(); - } - if (this.annotationIntrospector != null) { - this.objectMapper.setSerializationConfig( - this.objectMapper.getSerializationConfig().withAnnotationIntrospector(this.annotationIntrospector)); - this.objectMapper.setDeserializationConfig( - this.objectMapper.getDeserializationConfig().withAnnotationIntrospector(this.annotationIntrospector)); - } - if (this.dateFormat != null) { - this.objectMapper.setDateFormat(this.dateFormat); - } - for (Map.Entry entry : this.features.entrySet()) { - configureFeature(entry.getKey(), entry.getValue()); - } - } - - private void configureFeature(Object feature, boolean enabled) { - if (feature instanceof JsonParser.Feature) { - this.objectMapper.configure((JsonParser.Feature) feature, enabled); - } - else if (feature instanceof JsonGenerator.Feature) { - this.objectMapper.configure((JsonGenerator.Feature) feature, enabled); - } - else if (feature instanceof SerializationConfig.Feature) { - this.objectMapper.configure((SerializationConfig.Feature) feature, enabled); - } - else if (feature instanceof DeserializationConfig.Feature) { - this.objectMapper.configure((DeserializationConfig.Feature) feature, enabled); - } - else { - throw new IllegalArgumentException("Unknown feature class: " + feature.getClass().getName()); - } - } - - - /** - * Return the singleton ObjectMapper. - */ - @Override - public ObjectMapper getObject() { - return this.objectMapper; - } - - @Override - public Class getObjectType() { - return ObjectMapper.class; - } - - @Override - public boolean isSingleton() { - return true; - } - -} diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/MappingJacksonHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/json/MappingJacksonHttpMessageConverter.java deleted file mode 100644 index 19a29137c20..00000000000 --- a/spring-web/src/main/java/org/springframework/http/converter/json/MappingJacksonHttpMessageConverter.java +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * Licensed 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.springframework.http.converter.json; - -import java.io.IOException; -import java.lang.reflect.Type; -import java.nio.charset.Charset; - -import org.codehaus.jackson.JsonEncoding; -import org.codehaus.jackson.JsonGenerator; -import org.codehaus.jackson.JsonProcessingException; -import org.codehaus.jackson.map.ObjectMapper; -import org.codehaus.jackson.map.SerializationConfig; -import org.codehaus.jackson.type.JavaType; - -import org.springframework.http.HttpInputMessage; -import org.springframework.http.HttpOutputMessage; -import org.springframework.http.MediaType; -import org.springframework.http.converter.AbstractHttpMessageConverter; -import org.springframework.http.converter.GenericHttpMessageConverter; -import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.http.converter.HttpMessageNotWritableException; -import org.springframework.util.Assert; - -/** - * Implementation of {@link org.springframework.http.converter.HttpMessageConverter HttpMessageConverter} that - * can read and write JSON using Jackson 1.x's {@link ObjectMapper}. - * - *

This converter can be used to bind to typed beans, or untyped {@link java.util.HashMap HashMap} instances. - * - *

By default, this converter supports {@code application/json}. This can be overridden by setting the - * {@link #setSupportedMediaTypes supportedMediaTypes} property. - * - *

NOTE: Requires Jackson 1.8 or higher, as of Spring 4.0. - * At the same time, we strongly recommend a migration to Jackson 2.x! - * - * @author Arjen Poutsma - * @author Juergen Hoeller - * @since 3.0 - * @deprecated Please migrate to {@link MappingJackson2HttpMessageConverter} for Jackson 2.x. - */ -@Deprecated -public class MappingJacksonHttpMessageConverter extends AbstractHttpMessageConverter - implements GenericHttpMessageConverter { - - public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); - - - private ObjectMapper objectMapper = new ObjectMapper(); - - private String jsonPrefix; - - private Boolean prettyPrint; - - - /** - * Construct a new {@code MappingJacksonHttpMessageConverter}. - */ - public MappingJacksonHttpMessageConverter() { - super(new MediaType("application", "json", DEFAULT_CHARSET), - new MediaType("application", "*+json", DEFAULT_CHARSET)); - } - - - /** - * Set the {@code ObjectMapper} for this view. - * If not set, a default {@link ObjectMapper#ObjectMapper() ObjectMapper} is used. - *

Setting a custom-configured {@code ObjectMapper} is one way to take further control of the JSON - * serialization process. For example, an extended {@link org.codehaus.jackson.map.SerializerFactory} - * can be configured that provides custom serializers for specific types. The other option for refining - * the serialization process is to use Jackson's provided annotations on the types to be serialized, - * in which case a custom-configured ObjectMapper is unnecessary. - */ - public void setObjectMapper(ObjectMapper objectMapper) { - Assert.notNull(objectMapper, "ObjectMapper must not be null"); - this.objectMapper = objectMapper; - configurePrettyPrint(); - } - - /** - * Return the underlying {@code ObjectMapper} for this view. - */ - public ObjectMapper getObjectMapper() { - return this.objectMapper; - } - - /** - * Specify a custom prefix to use for this view's JSON output. - * Default is none. - * @see #setPrefixJson - */ - public void setJsonPrefix(String jsonPrefix) { - this.jsonPrefix = jsonPrefix; - } - - /** - * Indicate whether the JSON output by this view should be prefixed with "{} &&". Default is false. - *

Prefixing the JSON string in this manner is used to help prevent JSON Hijacking. - * The prefix renders the string syntactically invalid as a script so that it cannot be hijacked. - * This prefix does not affect the evaluation of JSON, but if JSON validation is performed on the - * string, the prefix would need to be ignored. - * @see #setJsonPrefix - */ - public void setPrefixJson(boolean prefixJson) { - this.jsonPrefix = (prefixJson ? "{} && " : null); - } - - /** - * Whether to use the {@link org.codehaus.jackson.util.DefaultPrettyPrinter} when writing JSON. - * This is a shortcut for setting up an {@code ObjectMapper} as follows: - *

-	 * ObjectMapper mapper = new ObjectMapper();
-	 * mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
-	 * converter.setObjectMapper(mapper);
-	 * 
- *

The default value is {@code false}. - */ - public void setPrettyPrint(boolean prettyPrint) { - this.prettyPrint = prettyPrint; - configurePrettyPrint(); - } - - private void configurePrettyPrint() { - if (this.prettyPrint != null) { - this.objectMapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, this.prettyPrint); - } - } - - - @Override - public boolean canRead(Class clazz, MediaType mediaType) { - return canRead(clazz, null, mediaType); - } - - @Override - public boolean canRead(Type type, Class contextClass, MediaType mediaType) { - JavaType javaType = getJavaType(type, contextClass); - return (this.objectMapper.canDeserialize(javaType) && canRead(mediaType)); - } - - @Override - public boolean canWrite(Class clazz, MediaType mediaType) { - return (this.objectMapper.canSerialize(clazz) && canWrite(mediaType)); - } - - @Override - protected boolean supports(Class clazz) { - // should not be called, since we override canRead/Write instead - throw new UnsupportedOperationException(); - } - - @Override - protected Object readInternal(Class clazz, HttpInputMessage inputMessage) - throws IOException, HttpMessageNotReadableException { - - JavaType javaType = getJavaType(clazz, null); - return readJavaType(javaType, inputMessage); - } - - @Override - public Object read(Type type, Class contextClass, HttpInputMessage inputMessage) - throws IOException, HttpMessageNotReadableException { - - JavaType javaType = getJavaType(type, contextClass); - return readJavaType(javaType, inputMessage); - } - - private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) { - try { - return this.objectMapper.readValue(inputMessage.getBody(), javaType); - } - catch (IOException ex) { - throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex); - } - } - - @Override - protected void writeInternal(Object object, HttpOutputMessage outputMessage) - throws IOException, HttpMessageNotWritableException { - - JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType()); - JsonGenerator jsonGenerator = - this.objectMapper.getJsonFactory().createJsonGenerator(outputMessage.getBody(), encoding); - - // A workaround for JsonGenerators not applying serialization features - // https://github.com/FasterXML/jackson-databind/issues/12 - if (this.objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.INDENT_OUTPUT)) { - jsonGenerator.useDefaultPrettyPrinter(); - } - - try { - if (this.jsonPrefix != null) { - jsonGenerator.writeRaw(this.jsonPrefix); - } - this.objectMapper.writeValue(jsonGenerator, object); - } - catch (JsonProcessingException ex) { - throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex); - } - } - - /** - * Return the Jackson {@link JavaType} for the specified type and context class. - *

The default implementation returns {@code typeFactory.constructType(type, contextClass)}, - * but this can be overridden in subclasses, to allow for custom generic collection handling. - * For instance: - *

-	 * protected JavaType getJavaType(Type type) {
-	 *   if (type instanceof Class && List.class.isAssignableFrom((Class)type)) {
-	 *     return TypeFactory.collectionType(ArrayList.class, MyBean.class);
-	 *   } else {
-	 *     return super.getJavaType(type);
-	 *   }
-	 * }
-	 * 
- * @param type the type to return the java type for - * @param contextClass a context class for the target type, for example a class - * in which the target type appears in a method signature, can be {@code null} - * @return the java type - */ - protected JavaType getJavaType(Type type, Class contextClass) { - return this.objectMapper.getTypeFactory().constructType(type, contextClass); - } - - /** - * Determine the JSON encoding to use for the given content type. - * @param contentType the media type as requested by the caller - * @return the JSON encoding to use (never {@code null}) - */ - protected JsonEncoding getJsonEncoding(MediaType contentType) { - if (contentType != null && contentType.getCharSet() != null) { - Charset charset = contentType.getCharSet(); - for (JsonEncoding encoding : JsonEncoding.values()) { - if (charset.name().equals(encoding.getJavaName())) { - return encoding; - } - } - } - return JsonEncoding.UTF8; - } - -} diff --git a/spring-web/src/main/java/org/springframework/http/converter/support/AllEncompassingFormHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/support/AllEncompassingFormHttpMessageConverter.java index ec1173d73fc..e4fd424f24c 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/support/AllEncompassingFormHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/support/AllEncompassingFormHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.http.converter.support; import javax.xml.transform.Source; @@ -39,10 +40,6 @@ public class AllEncompassingFormHttpMessageConverter extends FormHttpMessageConv ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", AllEncompassingFormHttpMessageConverter.class.getClassLoader()) && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", AllEncompassingFormHttpMessageConverter.class.getClassLoader()); - private static final boolean jacksonPresent = - ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", AllEncompassingFormHttpMessageConverter.class.getClassLoader()) && - ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator", AllEncompassingFormHttpMessageConverter.class.getClassLoader()); - @SuppressWarnings("deprecation") public AllEncompassingFormHttpMessageConverter() { @@ -53,9 +50,6 @@ public class AllEncompassingFormHttpMessageConverter extends FormHttpMessageConv if (jackson2Present) { addPartConverter(new MappingJackson2HttpMessageConverter()); } - else if (jacksonPresent) { - addPartConverter(new org.springframework.http.converter.json.MappingJacksonHttpMessageConverter()); - } } } diff --git a/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java b/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java index 0411c4791cb..9bdd8e41cd3 100644 --- a/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java +++ b/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java @@ -134,10 +134,6 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", RestTemplate.class.getClassLoader()) && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", RestTemplate.class.getClassLoader()); - private static final boolean jacksonPresent = - ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", RestTemplate.class.getClassLoader()) && - ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator", RestTemplate.class.getClassLoader()); - private final List> messageConverters = new ArrayList>(); @@ -157,6 +153,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat this.messageConverters.add(new ResourceHttpMessageConverter()); this.messageConverters.add(new SourceHttpMessageConverter()); this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); + if (romePresent) { this.messageConverters.add(new AtomFeedHttpMessageConverter()); this.messageConverters.add(new RssChannelHttpMessageConverter()); @@ -167,9 +164,6 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat if (jackson2Present) { this.messageConverters.add(new MappingJackson2HttpMessageConverter()); } - else if (jacksonPresent) { - this.messageConverters.add(new org.springframework.http.converter.json.MappingJacksonHttpMessageConverter()); - } } /** diff --git a/spring-web/src/test/java/org/springframework/http/converter/json/AbstractMappingJacksonHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/json/AbstractMappingJacksonHttpMessageConverterTests.java deleted file mode 100644 index e36244bfb8f..00000000000 --- a/spring-web/src/test/java/org/springframework/http/converter/json/AbstractMappingJacksonHttpMessageConverterTests.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * Licensed 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.springframework.http.converter.json; - -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Before; -import org.junit.Test; -import org.springframework.http.MediaType; -import org.springframework.http.MockHttpInputMessage; -import org.springframework.http.MockHttpOutputMessage; -import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.converter.HttpMessageNotReadableException; - -import static org.junit.Assert.*; - -/** - * Base class for Jackson and Jackson 2 converter tests. - * - * @author Arjen Poutsma - * @author Rossen Stoyanchev - */ -public abstract class AbstractMappingJacksonHttpMessageConverterTests> { - - protected static final String NEWLINE_SYSTEM_PROPERTY = System.getProperty("line.separator"); - - private T converter; - - @Before - public void setup() { - this.converter = createConverter(); - } - - protected T getConverter() { - return this.converter; - } - - protected abstract T createConverter(); - - @Test - public void canRead() { - assertTrue(converter.canRead(MyBean.class, new MediaType("application", "json"))); - assertTrue(converter.canRead(Map.class, new MediaType("application", "json"))); - } - - @Test - public void canWrite() { - assertTrue(converter.canWrite(MyBean.class, new MediaType("application", "json"))); - assertTrue(converter.canWrite(Map.class, new MediaType("application", "json"))); - } - - // SPR-7905 - - @Test - public void canReadAndWriteMicroformats() { - assertTrue(converter.canRead(MyBean.class, new MediaType("application", "vnd.test-micro-type+json"))); - assertTrue(converter.canWrite(MyBean.class, new MediaType("application", "vnd.test-micro-type+json"))); - } - - @Test - public void readTyped() throws IOException { - String body = - "{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"],\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}"; - MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8")); - inputMessage.getHeaders().setContentType(new MediaType("application", "json")); - MyBean result = (MyBean) converter.read(MyBean.class, inputMessage); - assertEquals("Foo", result.getString()); - assertEquals(42, result.getNumber()); - assertEquals(42F, result.getFraction(), 0F); - assertArrayEquals(new String[]{"Foo", "Bar"}, result.getArray()); - assertTrue(result.isBool()); - assertArrayEquals(new byte[]{0x1, 0x2}, result.getBytes()); - } - - @Test - @SuppressWarnings("unchecked") - public void readUntyped() throws IOException { - String body = - "{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"],\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}"; - MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8")); - inputMessage.getHeaders().setContentType(new MediaType("application", "json")); - HashMap result = (HashMap) converter.read(HashMap.class, inputMessage); - assertEquals("Foo", result.get("string")); - assertEquals(42, result.get("number")); - assertEquals(42D, (Double) result.get("fraction"), 0D); - List array = new ArrayList(); - array.add("Foo"); - array.add("Bar"); - assertEquals(array, result.get("array")); - assertEquals(Boolean.TRUE, result.get("bool")); - assertEquals("AQI=", result.get("bytes")); - } - - @Test - public void write() throws IOException { - MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); - MyBean body = new MyBean(); - body.setString("Foo"); - body.setNumber(42); - body.setFraction(42F); - body.setArray(new String[]{"Foo", "Bar"}); - body.setBool(true); - body.setBytes(new byte[]{0x1, 0x2}); - converter.write(body, null, outputMessage); - Charset utf8 = Charset.forName("UTF-8"); - String result = outputMessage.getBodyAsString(utf8); - assertTrue(result.contains("\"string\":\"Foo\"")); - assertTrue(result.contains("\"number\":42")); - assertTrue(result.contains("fraction\":42.0")); - assertTrue(result.contains("\"array\":[\"Foo\",\"Bar\"]")); - assertTrue(result.contains("\"bool\":true")); - assertTrue(result.contains("\"bytes\":\"AQI=\"")); - assertEquals("Invalid content-type", new MediaType("application", "json", utf8), - outputMessage.getHeaders().getContentType()); - } - - @Test - public void writeUTF16() throws IOException { - Charset utf16 = Charset.forName("UTF-16BE"); - MediaType contentType = new MediaType("application", "json", utf16); - MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); - String body = "H\u00e9llo W\u00f6rld"; - converter.write(body, contentType, outputMessage); - assertEquals("Invalid result", "\"" + body + "\"", outputMessage.getBodyAsString(utf16)); - assertEquals("Invalid content-type", contentType, outputMessage.getHeaders().getContentType()); - } - - @Test(expected = HttpMessageNotReadableException.class) - public void readInvalidJson() throws IOException { - String body = "FooBar"; - MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8")); - inputMessage.getHeaders().setContentType(new MediaType("application", "json")); - converter.read(MyBean.class, inputMessage); - } - - @Test(expected = HttpMessageNotReadableException.class) - public void readValidJsonWithUnknownProperty() throws IOException { - String body = "{\"string\":\"string\",\"unknownProperty\":\"value\"}"; - MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8")); - inputMessage.getHeaders().setContentType(new MediaType("application", "json")); - converter.read(MyBean.class, inputMessage); - } - - public static class MyBean { - - private String string; - - private int number; - - private float fraction; - - private String[] array; - - private boolean bool; - - private byte[] bytes; - - public byte[] getBytes() { - return bytes; - } - - public void setBytes(byte[] bytes) { - this.bytes = bytes; - } - - public boolean isBool() { - return bool; - } - - public void setBool(boolean bool) { - this.bool = bool; - } - - public String getString() { - return string; - } - - public void setString(String string) { - this.string = string; - } - - public int getNumber() { - return number; - } - - public void setNumber(int number) { - this.number = number; - } - - public float getFraction() { - return fraction; - } - - public void setFraction(float fraction) { - this.fraction = fraction; - } - - public String[] getArray() { - return array; - } - - public void setArray(String[] array) { - this.array = array; - } - } - -} diff --git a/spring-web/src/test/java/org/springframework/http/converter/json/JacksonObjectMapperFactoryBeanTests.java b/spring-web/src/test/java/org/springframework/http/converter/json/JacksonObjectMapperFactoryBeanTests.java deleted file mode 100644 index a66f4e2fbb8..00000000000 --- a/spring-web/src/test/java/org/springframework/http/converter/json/JacksonObjectMapperFactoryBeanTests.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2002-2012 the original author or authors. - * - * Licensed 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.springframework.http.converter.json; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.text.SimpleDateFormat; - -import org.codehaus.jackson.JsonFactory; -import org.codehaus.jackson.JsonGenerator; -import org.codehaus.jackson.JsonParser; -import org.codehaus.jackson.map.DeserializationConfig; -import org.codehaus.jackson.map.ObjectMapper; -import org.codehaus.jackson.map.SerializationConfig; -import org.codehaus.jackson.map.introspect.NopAnnotationIntrospector; -import org.junit.Before; -import org.junit.Test; - -/** - * @author Dmitry Katsubo - */ -public class JacksonObjectMapperFactoryBeanTests { - - private static final String DATE_FORMAT = "yyyy-MM-dd"; - - private JacksonObjectMapperFactoryBean factory; - - @Before - public void setUp() { - factory = new JacksonObjectMapperFactoryBean(); - } - - @Test - public void testSetFeaturesToEnableEmpty() { - factory.setFeaturesToEnable(new Object[0]); - factory.setFeaturesToDisable(new Object[0]); - } - - @Test(expected = IllegalArgumentException.class) - public void testUnknownFeature() { - factory.setFeaturesToEnable(new Object[] { Boolean.TRUE }); - factory.afterPropertiesSet(); - } - - @Test - public void testBooleanSetters() { - factory.setAutoDetectFields(false); - factory.setAutoDetectGettersSetters(false); - factory.setFailOnEmptyBeans(false); - factory.setIndentOutput(true); - - factory.afterPropertiesSet(); - - ObjectMapper objectMapper = factory.getObject(); - - SerializationConfig serializeConfig = objectMapper.getSerializationConfig(); - DeserializationConfig deserializeConfig = objectMapper.getDeserializationConfig(); - - assertFalse(serializeConfig.isEnabled(SerializationConfig.Feature.AUTO_DETECT_FIELDS)); - assertFalse(deserializeConfig.isEnabled(DeserializationConfig.Feature.AUTO_DETECT_FIELDS)); - assertFalse(serializeConfig.isEnabled(SerializationConfig.Feature.AUTO_DETECT_GETTERS)); - assertFalse(deserializeConfig.isEnabled(DeserializationConfig.Feature.AUTO_DETECT_SETTERS)); - assertFalse(serializeConfig.isEnabled(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS)); - assertTrue(serializeConfig.isEnabled(SerializationConfig.Feature.INDENT_OUTPUT)); - } - - @Test - public void testDateTimeFormatSetter() { - SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT); - - factory.setDateFormat(dateFormat); - factory.afterPropertiesSet(); - - assertEquals(dateFormat, factory.getObject().getSerializationConfig().getDateFormat()); - } - - @Test - public void testSimpleDateFormatStringSetter() { - SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT); - - factory.setSimpleDateFormat(DATE_FORMAT); - factory.afterPropertiesSet(); - - assertEquals(dateFormat, factory.getObject().getSerializationConfig().getDateFormat()); - } - - @Test - public void testSimpleSetup() { - factory.afterPropertiesSet(); - - assertNotNull(factory.getObject()); - assertTrue(factory.isSingleton()); - assertEquals(ObjectMapper.class, factory.getObjectType()); - } - - @Test - public void testCompleteSetup() { - NopAnnotationIntrospector annotationIntrospector = new NopAnnotationIntrospector(); - ObjectMapper objectMapper = new ObjectMapper(); - - assertTrue(factory.isSingleton()); - assertEquals(ObjectMapper.class, factory.getObjectType()); - - factory.setObjectMapper(objectMapper); - factory.setAnnotationIntrospector(annotationIntrospector); - factory.setFeaturesToEnable(new Object[] { - SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, - DeserializationConfig.Feature.USE_ANNOTATIONS, - JsonParser.Feature.ALLOW_SINGLE_QUOTES, - JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS - }); - factory.setFeaturesToDisable(new Object[] { - SerializationConfig.Feature.AUTO_DETECT_GETTERS, - DeserializationConfig.Feature.AUTO_DETECT_FIELDS, - JsonParser.Feature.AUTO_CLOSE_SOURCE, - JsonGenerator.Feature.QUOTE_FIELD_NAMES - }); - - factory.afterPropertiesSet(); - - assertTrue(objectMapper == factory.getObject()); - - SerializationConfig serializeConfig = objectMapper.getSerializationConfig(); - DeserializationConfig deserializeConfig = objectMapper.getDeserializationConfig(); - JsonFactory jsonFactory = objectMapper.getJsonFactory(); - - assertTrue(annotationIntrospector == serializeConfig.getAnnotationIntrospector()); - assertTrue(annotationIntrospector == deserializeConfig.getAnnotationIntrospector()); - - assertTrue(serializeConfig.isEnabled(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS)); - assertTrue(deserializeConfig.isEnabled(DeserializationConfig.Feature.USE_ANNOTATIONS)); - assertTrue(jsonFactory.isEnabled(JsonParser.Feature.ALLOW_SINGLE_QUOTES)); - assertTrue(jsonFactory.isEnabled(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS)); - - assertFalse(serializeConfig.isEnabled(SerializationConfig.Feature.AUTO_DETECT_GETTERS)); - assertFalse(deserializeConfig.isEnabled(DeserializationConfig.Feature.AUTO_DETECT_FIELDS)); - assertFalse(jsonFactory.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)); - assertFalse(jsonFactory.isEnabled(JsonGenerator.Feature.QUOTE_FIELD_NAMES)); - } - -} diff --git a/spring-web/src/test/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverterTests.java index 03aad0272a8..e741202422e 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,16 +20,19 @@ import java.io.IOException; import java.lang.reflect.Type; import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.Test; + import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.MediaType; import org.springframework.http.MockHttpInputMessage; import org.springframework.http.MockHttpOutputMessage; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.http.converter.HttpMessageNotReadableException; import static org.junit.Assert.*; @@ -38,12 +41,115 @@ import static org.junit.Assert.*; * * @author Rossen Stoyanchev */ -public class MappingJackson2HttpMessageConverterTests extends AbstractMappingJacksonHttpMessageConverterTests { +public class MappingJackson2HttpMessageConverterTests { + + protected static final String NEWLINE_SYSTEM_PROPERTY = System.getProperty("line.separator"); + + private final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); + + + @Test + public void canRead() { + assertTrue(converter.canRead(MyBean.class, new MediaType("application", "json"))); + assertTrue(converter.canRead(Map.class, new MediaType("application", "json"))); + } + + @Test + public void canWrite() { + assertTrue(converter.canWrite(MyBean.class, new MediaType("application", "json"))); + assertTrue(converter.canWrite(Map.class, new MediaType("application", "json"))); + } + + // SPR-7905 + + @Test + public void canReadAndWriteMicroformats() { + assertTrue(converter.canRead(MyBean.class, new MediaType("application", "vnd.test-micro-type+json"))); + assertTrue(converter.canWrite(MyBean.class, new MediaType("application", "vnd.test-micro-type+json"))); + } + + @Test + public void readTyped() throws IOException { + String body = + "{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"],\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}"; + MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8")); + inputMessage.getHeaders().setContentType(new MediaType("application", "json")); + MyBean result = (MyBean) converter.read(MyBean.class, inputMessage); + assertEquals("Foo", result.getString()); + assertEquals(42, result.getNumber()); + assertEquals(42F, result.getFraction(), 0F); + assertArrayEquals(new String[]{"Foo", "Bar"}, result.getArray()); + assertTrue(result.isBool()); + assertArrayEquals(new byte[]{0x1, 0x2}, result.getBytes()); + } + + @Test + @SuppressWarnings("unchecked") + public void readUntyped() throws IOException { + String body = + "{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"],\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}"; + MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8")); + inputMessage.getHeaders().setContentType(new MediaType("application", "json")); + HashMap result = (HashMap) converter.read(HashMap.class, inputMessage); + assertEquals("Foo", result.get("string")); + assertEquals(42, result.get("number")); + assertEquals(42D, (Double) result.get("fraction"), 0D); + List array = new ArrayList(); + array.add("Foo"); + array.add("Bar"); + assertEquals(array, result.get("array")); + assertEquals(Boolean.TRUE, result.get("bool")); + assertEquals("AQI=", result.get("bytes")); + } + + @Test + public void write() throws IOException { + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + MyBean body = new MyBean(); + body.setString("Foo"); + body.setNumber(42); + body.setFraction(42F); + body.setArray(new String[]{"Foo", "Bar"}); + body.setBool(true); + body.setBytes(new byte[]{0x1, 0x2}); + converter.write(body, null, outputMessage); + Charset utf8 = Charset.forName("UTF-8"); + String result = outputMessage.getBodyAsString(utf8); + assertTrue(result.contains("\"string\":\"Foo\"")); + assertTrue(result.contains("\"number\":42")); + assertTrue(result.contains("fraction\":42.0")); + assertTrue(result.contains("\"array\":[\"Foo\",\"Bar\"]")); + assertTrue(result.contains("\"bool\":true")); + assertTrue(result.contains("\"bytes\":\"AQI=\"")); + assertEquals("Invalid content-type", new MediaType("application", "json", utf8), + outputMessage.getHeaders().getContentType()); + } + @Test + public void writeUTF16() throws IOException { + Charset utf16 = Charset.forName("UTF-16BE"); + MediaType contentType = new MediaType("application", "json", utf16); + MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); + String body = "H\u00e9llo W\u00f6rld"; + converter.write(body, contentType, outputMessage); + assertEquals("Invalid result", "\"" + body + "\"", outputMessage.getBodyAsString(utf16)); + assertEquals("Invalid content-type", contentType, outputMessage.getHeaders().getContentType()); + } - @Override - protected MappingJackson2HttpMessageConverter createConverter() { - return new MappingJackson2HttpMessageConverter(); + @Test(expected = HttpMessageNotReadableException.class) + public void readInvalidJson() throws IOException { + String body = "FooBar"; + MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8")); + inputMessage.getHeaders().setContentType(new MediaType("application", "json")); + converter.read(MyBean.class, inputMessage); + } + + @Test(expected = HttpMessageNotReadableException.class) + public void readValidJsonWithUnknownProperty() throws IOException { + String body = "{\"string\":\"string\",\"unknownProperty\":\"value\"}"; + MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8")); + inputMessage.getHeaders().setContentType(new MediaType("application", "json")); + converter.read(MyBean.class, inputMessage); } @Test @@ -106,8 +212,8 @@ public class MappingJackson2HttpMessageConverterTests extends AbstractMappingJac PrettyPrintBean bean = new PrettyPrintBean(); bean.setName("Jason"); - getConverter().setPrettyPrint(true); - getConverter().writeInternal(bean, outputMessage); + this.converter.setPrettyPrint(true); + this.converter.writeInternal(bean, outputMessage); String result = outputMessage.getBodyAsString(Charset.forName("UTF-8")); assertEquals("{" + NEWLINE_SYSTEM_PROPERTY + " \"name\" : \"Jason\"" + NEWLINE_SYSTEM_PROPERTY + "}", result); @@ -116,8 +222,8 @@ public class MappingJackson2HttpMessageConverterTests extends AbstractMappingJac @Test public void prefixJson() throws Exception { MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); - getConverter().setPrefixJson(true); - getConverter().writeInternal("foo", outputMessage); + this.converter.setPrefixJson(true); + this.converter.writeInternal("foo", outputMessage); assertEquals("{} && \"foo\"", outputMessage.getBodyAsString(Charset.forName("UTF-8"))); } @@ -125,13 +231,77 @@ public class MappingJackson2HttpMessageConverterTests extends AbstractMappingJac @Test public void prefixJsonCustom() throws Exception { MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); - getConverter().setJsonPrefix(")]}',"); - getConverter().writeInternal("foo", outputMessage); + this.converter.setJsonPrefix(")]}',"); + this.converter.writeInternal("foo", outputMessage); assertEquals(")]}',\"foo\"", outputMessage.getBodyAsString(Charset.forName("UTF-8"))); } + public static class MyBean { + + private String string; + + private int number; + + private float fraction; + + private String[] array; + + private boolean bool; + + private byte[] bytes; + + public byte[] getBytes() { + return bytes; + } + + public void setBytes(byte[] bytes) { + this.bytes = bytes; + } + + public boolean isBool() { + return bool; + } + + public void setBool(boolean bool) { + this.bool = bool; + } + + public String getString() { + return string; + } + + public void setString(String string) { + this.string = string; + } + + public int getNumber() { + return number; + } + + public void setNumber(int number) { + this.number = number; + } + + public float getFraction() { + return fraction; + } + + public void setFraction(float fraction) { + this.fraction = fraction; + } + + public String[] getArray() { + return array; + } + + public void setArray(String[] array) { + this.array = array; + } + } + + public static class PrettyPrintBean { private String name; diff --git a/spring-web/src/test/java/org/springframework/http/converter/json/MappingJacksonHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/json/MappingJacksonHttpMessageConverterTests.java deleted file mode 100644 index 08d20668b99..00000000000 --- a/spring-web/src/test/java/org/springframework/http/converter/json/MappingJacksonHttpMessageConverterTests.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2002-2012 the original author or authors. - * - * Licensed 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.springframework.http.converter.json; - -import java.io.IOException; -import java.lang.reflect.Type; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; - -import org.codehaus.jackson.map.type.TypeFactory; -import org.codehaus.jackson.type.JavaType; -import org.junit.Test; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.MediaType; -import org.springframework.http.MockHttpInputMessage; -import org.springframework.http.MockHttpOutputMessage; - -import static org.junit.Assert.*; - -/** - * Jackson 1.x converter tests. - * - * @author Rossen Stoyanchev - */ -public class MappingJacksonHttpMessageConverterTests extends AbstractMappingJacksonHttpMessageConverterTests { - - @Override - protected MappingJacksonHttpMessageConverter createConverter() { - return new MappingJacksonHttpMessageConverter(); - } - - @Test - @SuppressWarnings("unchecked") - public void readGenerics() throws IOException { - MappingJacksonHttpMessageConverter converter = new MappingJacksonHttpMessageConverter() { - @Override - protected JavaType getJavaType(Type type, Class contextClass) { - if (type instanceof Class && List.class.isAssignableFrom((Class)type)) { - return TypeFactory.collectionType(ArrayList.class, MyBean.class); - } - else { - return super.getJavaType(type, contextClass); - } - } - }; - String body = - "[{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"],\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}]"; - MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8")); - inputMessage.getHeaders().setContentType(new MediaType("application", "json")); - - List results = (List) converter.read(List.class, inputMessage); - assertEquals(1, results.size()); - MyBean result = results.get(0); - assertEquals("Foo", result.getString()); - assertEquals(42, result.getNumber()); - assertEquals(42F, result.getFraction(), 0F); - assertArrayEquals(new String[]{"Foo", "Bar"}, result.getArray()); - assertTrue(result.isBool()); - assertArrayEquals(new byte[]{0x1, 0x2}, result.getBytes()); - } - - @Test - @SuppressWarnings("unchecked") - public void readParameterizedType() throws IOException { - ParameterizedTypeReference> beansList = new ParameterizedTypeReference>() {}; - - String body = - "[{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"],\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}]"; - MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8")); - inputMessage.getHeaders().setContentType(new MediaType("application", "json")); - - MappingJacksonHttpMessageConverter converter = new MappingJacksonHttpMessageConverter(); - List results = (List) converter.read(beansList.getType(), null, inputMessage); - assertEquals(1, results.size()); - MyBean result = results.get(0); - assertEquals("Foo", result.getString()); - assertEquals(42, result.getNumber()); - assertEquals(42F, result.getFraction(), 0F); - assertArrayEquals(new String[]{"Foo", "Bar"}, result.getArray()); - assertTrue(result.isBool()); - assertArrayEquals(new byte[]{0x1, 0x2}, result.getBytes()); - } - - @Test - public void prettyPrint() throws Exception { - MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); - PrettyPrintBean bean = new PrettyPrintBean(); - bean.setName("Jason"); - - getConverter().setPrettyPrint(true); - getConverter().writeInternal(bean, outputMessage); - String result = outputMessage.getBodyAsString(Charset.forName("UTF-8")); - - assertEquals("{" + NEWLINE_SYSTEM_PROPERTY + " \"name\" : \"Jason\"" + NEWLINE_SYSTEM_PROPERTY + "}", result); - } - - @Test - public void prefixJson() throws Exception { - MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); - getConverter().setPrefixJson(true); - getConverter().writeInternal("foo", outputMessage); - - assertEquals("{} && \"foo\"", outputMessage.getBodyAsString(Charset.forName("UTF-8"))); - } - - @Test - public void prefixJsonCustom() throws Exception { - MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); - getConverter().setJsonPrefix(")]}',"); - getConverter().writeInternal("foo", outputMessage); - - assertEquals(")]}',\"foo\"", outputMessage.getBodyAsString(Charset.forName("UTF-8"))); - } - - - public static class PrettyPrintBean { - - private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - } - -} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java index ebab71b9238..117ae1fd6de 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java @@ -133,6 +133,9 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { private static final boolean javaxValidationPresent = ClassUtils.isPresent( "javax.validation.Validator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader()); + private static boolean romePresent = + ClassUtils.isPresent("com.sun.syndication.feed.WireFeed", AnnotationDrivenBeanDefinitionParser.class.getClassLoader()); + private static final boolean jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", AnnotationDrivenBeanDefinitionParser.class.getClassLoader()); @@ -140,13 +143,6 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", AnnotationDrivenBeanDefinitionParser.class.getClassLoader()) && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader()); - private static final boolean jacksonPresent = - ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", AnnotationDrivenBeanDefinitionParser.class.getClassLoader()) && - ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader()); - - private static boolean romePresent = - ClassUtils.isPresent("com.sun.syndication.feed.WireFeed", AnnotationDrivenBeanDefinitionParser.class.getClassLoader()); - @Override public BeanDefinition parse(Element element, ParserContext parserContext) { @@ -338,7 +334,6 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { } private void configurePathMatchingProperties(RootBeanDefinition handlerMappingDef, Element element) { - Element pathMatchingElement = DomUtils.getChildElementByTagName(element, "path-matching"); if(pathMatchingElement != null) { if (pathMatchingElement.hasAttribute("suffix-pattern")) { @@ -370,12 +365,12 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { props.put("atom", MediaType.APPLICATION_ATOM_XML_VALUE); props.put("rss", "application/rss+xml"); } - if (jackson2Present || jacksonPresent) { - props.put("json", MediaType.APPLICATION_JSON_VALUE); - } if (jaxb2Present) { props.put("xml", MediaType.APPLICATION_XML_VALUE); } + if (jackson2Present) { + props.put("json", MediaType.APPLICATION_JSON_VALUE); + } return props; } @@ -485,14 +480,9 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { if (jaxb2Present) { messageConverters.add(createConverterDefinition(Jaxb2RootElementHttpMessageConverter.class, source)); } - if (jackson2Present) { messageConverters.add(createConverterDefinition(MappingJackson2HttpMessageConverter.class, source)); } - else if (jacksonPresent) { - messageConverters.add(createConverterDefinition( - org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.class, source)); - } } return messageConverters; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java index 706babc0d01..aa17ca77782 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java @@ -20,7 +20,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; - import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.xml.transform.Source; @@ -143,6 +142,9 @@ import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolv */ public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware { + private static boolean romePresent = + ClassUtils.isPresent("com.sun.syndication.feed.WireFeed", WebMvcConfigurationSupport.class.getClassLoader()); + private static final boolean jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", WebMvcConfigurationSupport.class.getClassLoader()); @@ -150,12 +152,6 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", WebMvcConfigurationSupport.class.getClassLoader()) && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", WebMvcConfigurationSupport.class.getClassLoader()); - private static final boolean jacksonPresent = - ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", WebMvcConfigurationSupport.class.getClassLoader()) && - ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator", WebMvcConfigurationSupport.class.getClassLoader()); - - private static boolean romePresent = - ClassUtils.isPresent("com.sun.syndication.feed.WireFeed", WebMvcConfigurationSupport.class.getClassLoader()); private ServletContext servletContext; @@ -225,9 +221,9 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv InterceptorRegistry registry = new InterceptorRegistry(); addInterceptors(registry); registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService())); - interceptors = registry.getInterceptors(); + this.interceptors = registry.getInterceptors(); } - return interceptors.toArray(); + return this.interceptors.toArray(); } /** @@ -264,12 +260,12 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv map.put("atom", MediaType.APPLICATION_ATOM_XML); map.put("rss", MediaType.valueOf("application/rss+xml")); } - if (jackson2Present || jacksonPresent) { - map.put("json", MediaType.APPLICATION_JSON); - } if (jaxb2Present) { map.put("xml", MediaType.APPLICATION_XML); } + if (jackson2Present) { + map.put("json", MediaType.APPLICATION_JSON); + } return map; } @@ -552,6 +548,7 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv messageConverters.add(new ResourceHttpMessageConverter()); messageConverters.add(new SourceHttpMessageConverter()); messageConverters.add(new AllEncompassingFormHttpMessageConverter()); + if (romePresent) { messageConverters.add(new AtomFeedHttpMessageConverter()); messageConverters.add(new RssChannelHttpMessageConverter()); @@ -562,9 +559,6 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv if (jackson2Present) { messageConverters.add(new MappingJackson2HttpMessageConverter()); } - else if (jacksonPresent) { - messageConverters.add(new org.springframework.http.converter.json.MappingJacksonHttpMessageConverter()); - } } /** diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/json/MappingJackson2JsonView.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/json/MappingJackson2JsonView.java index 93c9e10698a..a744e4a73c7 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/json/MappingJackson2JsonView.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/json/MappingJackson2JsonView.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -209,7 +209,7 @@ public class MappingJackson2JsonView extends AbstractView { /** * Set whether to serialize models containing a single attribute as a map or whether to * extract the single value from the model and serialize it directly. - *

The effect of setting this flag is similar to using {@code MappingJacksonHttpMessageConverter} + *

The effect of setting this flag is similar to using {@code MappingJackson2HttpMessageConverter} * with an {@code @ResponseBody} request-handling method. *

Default is {@code false}. */ diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/json/MappingJacksonJsonView.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/json/MappingJacksonJsonView.java deleted file mode 100644 index 95453a88b7d..00000000000 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/json/MappingJacksonJsonView.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * Licensed 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.springframework.web.servlet.view.json; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.codehaus.jackson.JsonEncoding; -import org.codehaus.jackson.JsonGenerator; -import org.codehaus.jackson.map.ObjectMapper; -import org.codehaus.jackson.map.SerializationConfig; - -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.validation.BindingResult; -import org.springframework.web.servlet.View; -import org.springframework.web.servlet.view.AbstractView; - -/** - * Spring MVC {@link View} that renders JSON content by serializing the model for the current request - * using Jackson 1.x's {@link ObjectMapper}. - * - *

By default, the entire contents of the model map (with the exception of framework-specific classes) - * will be encoded as JSON. If the model contains only one key, you can have it extracted encoded as JSON - * alone via {@link #setExtractValueFromSingleKeyModel}. - * - * @author Jeremy Grelle - * @author Arjen Poutsma - * @author Rossen Stoyanchev - * @author Juergen Hoeller - * @since 3.0 - * @deprecated Please migrate to {@link MappingJackson2JsonView} for Jackson 2.x. - */ -@Deprecated -public class MappingJacksonJsonView extends AbstractView { - - /** - * Default content type: "application/json". - * Overridable through {@link #setContentType}. - */ - public static final String DEFAULT_CONTENT_TYPE = "application/json"; - - - private ObjectMapper objectMapper = new ObjectMapper(); - - private JsonEncoding encoding = JsonEncoding.UTF8; - - private String jsonPrefix; - - private Boolean prettyPrint; - - private Set modelKeys; - - private boolean extractValueFromSingleKeyModel = false; - - private boolean disableCaching = true; - - private boolean updateContentLength = false; - - - /** - * Construct a new {@code MappingJacksonJsonView}, setting the content type to {@code application/json}. - */ - public MappingJacksonJsonView() { - setContentType(DEFAULT_CONTENT_TYPE); - setExposePathVariables(false); - } - - - /** - * Set the {@code ObjectMapper} for this view. - * If not set, a default {@link ObjectMapper#ObjectMapper() ObjectMapper} will be used. - *

Setting a custom-configured {@code ObjectMapper} is one way to take further control of - * the JSON serialization process. The other option is to use Jackson's provided annotations - * on the types to be serialized, in which case a custom-configured ObjectMapper is unnecessary. - */ - public void setObjectMapper(ObjectMapper objectMapper) { - Assert.notNull(objectMapper, "'objectMapper' must not be null"); - this.objectMapper = objectMapper; - configurePrettyPrint(); - } - - /** - * Return the {@code ObjectMapper} for this view. - */ - public final ObjectMapper getObjectMapper() { - return this.objectMapper; - } - - /** - * Set the {@code JsonEncoding} for this view. - * By default, {@linkplain JsonEncoding#UTF8 UTF-8} is used. - */ - public void setEncoding(JsonEncoding encoding) { - Assert.notNull(encoding, "'encoding' must not be null"); - this.encoding = encoding; - } - - /** - * Return the {@code JsonEncoding} for this view. - */ - public final JsonEncoding getEncoding() { - return this.encoding; - } - - /** - * Specify a custom prefix to use for this view's JSON output. - * Default is none. - * @see #setPrefixJson - */ - public void setJsonPrefix(String jsonPrefix) { - this.jsonPrefix = jsonPrefix; - } - - /** - * Indicates whether the JSON output by this view should be prefixed with "{} && ". - * Default is {@code false}. - *

Prefixing the JSON string in this manner is used to help prevent JSON Hijacking. - * The prefix renders the string syntactically invalid as a script so that it cannot be hijacked. - * This prefix does not affect the evaluation of JSON, but if JSON validation is performed - * on the string, the prefix would need to be ignored. - * @see #setJsonPrefix - */ - public void setPrefixJson(boolean prefixJson) { - this.jsonPrefix = (prefixJson ? "{} && " : null); - } - - /** - * Whether to use the default pretty printer when writing JSON. - * This is a shortcut for setting up an {@code ObjectMapper} as follows: - *

-	 * ObjectMapper mapper = new ObjectMapper();
-	 * mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
-	 * 
- *

The default value is {@code false}. - */ - public void setPrettyPrint(boolean prettyPrint) { - this.prettyPrint = prettyPrint; - configurePrettyPrint(); - } - - private void configurePrettyPrint() { - if (this.prettyPrint != null) { - this.objectMapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, this.prettyPrint); - } - } - - /** - * Set the attribute in the model that should be rendered by this view. - * When set, all other model attributes will be ignored. - */ - public void setModelKey(String modelKey) { - this.modelKeys = Collections.singleton(modelKey); - } - - /** - * Set the attributes in the model that should be rendered by this view. - * When set, all other model attributes will be ignored. - */ - public void setModelKeys(Set modelKeys) { - this.modelKeys = modelKeys; - } - - /** - * Return the attributes in the model that should be rendered by this view. - */ - public final Set getModelKeys() { - return this.modelKeys; - } - - /** - * Set the attributes in the model that should be rendered by this view. - * When set, all other model attributes will be ignored. - * @deprecated use {@link #setModelKeys(Set)} instead - */ - @Deprecated - public void setRenderedAttributes(Set renderedAttributes) { - this.modelKeys = renderedAttributes; - } - - /** - * Return the attributes in the model that should be rendered by this view. - * @deprecated use {@link #getModelKeys()} instead - */ - @Deprecated - public final Set getRenderedAttributes() { - return this.modelKeys; - } - - /** - * Set whether to serialize models containing a single attribute as a map or whether to - * extract the single value from the model and serialize it directly. - *

The effect of setting this flag is similar to using {@code MappingJacksonHttpMessageConverter} - * with an {@code @ResponseBody} request-handling method. - *

Default is {@code false}. - */ - public void setExtractValueFromSingleKeyModel(boolean extractValueFromSingleKeyModel) { - this.extractValueFromSingleKeyModel = extractValueFromSingleKeyModel; - } - - /** - * Disables caching of the generated JSON. - *

Default is {@code true}, which will prevent the client from caching the generated JSON. - */ - public void setDisableCaching(boolean disableCaching) { - this.disableCaching = disableCaching; - } - - /** - * Whether to update the 'Content-Length' header of the response. When set to - * {@code true}, the response is buffered in order to determine the content - * length and set the 'Content-Length' header of the response. - *

The default setting is {@code false}. - */ - public void setUpdateContentLength(boolean updateContentLength) { - this.updateContentLength = updateContentLength; - } - - - @Override - protected void prepareResponse(HttpServletRequest request, HttpServletResponse response) { - setResponseContentType(request, response); - response.setCharacterEncoding(this.encoding.getJavaName()); - if (this.disableCaching) { - response.addHeader("Pragma", "no-cache"); - response.addHeader("Cache-Control", "no-cache, no-store, max-age=0"); - response.addDateHeader("Expires", 1L); - } - } - - @Override - protected void renderMergedOutputModel(Map model, HttpServletRequest request, - HttpServletResponse response) throws Exception { - - OutputStream stream = (this.updateContentLength ? createTemporaryOutputStream() : response.getOutputStream()); - Object value = filterModel(model); - writeContent(stream, value, this.jsonPrefix); - if (this.updateContentLength) { - writeToResponse(response, (ByteArrayOutputStream) stream); - } - } - - /** - * Filter out undesired attributes from the given model. - * The return value can be either another {@link Map} or a single value object. - *

The default implementation removes {@link BindingResult} instances and entries - * not included in the {@link #setRenderedAttributes renderedAttributes} property. - * @param model the model, as passed on to {@link #renderMergedOutputModel} - * @return the value to be rendered - */ - protected Object filterModel(Map model) { - Map result = new HashMap(model.size()); - Set renderedAttributes = (!CollectionUtils.isEmpty(this.modelKeys) ? this.modelKeys : model.keySet()); - for (Map.Entry entry : model.entrySet()) { - if (!(entry.getValue() instanceof BindingResult) && renderedAttributes.contains(entry.getKey())) { - result.put(entry.getKey(), entry.getValue()); - } - } - return (this.extractValueFromSingleKeyModel && result.size() == 1 ? result.values().iterator().next() : result); - } - - /** - * Write the actual JSON content to the stream. - * @param stream the output stream to use - * @param value the value to be rendered, as returned from {@link #filterModel} - * @param jsonPrefix the prefix for this view's JSON output - * (as indicated through {@link #setJsonPrefix}/{@link #setPrefixJson}) - * @throws IOException if writing failed - */ - protected void writeContent(OutputStream stream, Object value, String jsonPrefix) throws IOException { - JsonGenerator generator = this.objectMapper.getJsonFactory().createJsonGenerator(stream, this.encoding); - - // A workaround for JsonGenerators not applying serialization features - // https://github.com/FasterXML/jackson-databind/issues/12 - if (this.objectMapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.INDENT_OUTPUT)) { - generator.useDefaultPrettyPrinter(); - } - - if (jsonPrefix != null) { - generator.writeRaw(jsonPrefix); - } - this.objectMapper.writeValue(generator, value); - } - -} diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/json/MappingJacksonJsonViewTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/json/MappingJacksonJsonViewTests.java deleted file mode 100644 index 92aa6fc8fc4..00000000000 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/json/MappingJacksonJsonViewTests.java +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Copyright 2002-2014 the original author or authors. - * - * Licensed 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.springframework.web.servlet.view.json; - -import java.io.IOException; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.codehaus.jackson.JsonGenerator; -import org.codehaus.jackson.map.BeanProperty; -import org.codehaus.jackson.map.JsonMappingException; -import org.codehaus.jackson.map.JsonSerializer; -import org.codehaus.jackson.map.ObjectMapper; -import org.codehaus.jackson.map.SerializationConfig; -import org.codehaus.jackson.map.SerializerFactory; -import org.codehaus.jackson.map.SerializerProvider; -import org.codehaus.jackson.map.annotate.JsonSerialize; -import org.codehaus.jackson.map.ser.BeanSerializerFactory; -import org.codehaus.jackson.type.JavaType; -import org.junit.Before; -import org.junit.Test; -import org.mozilla.javascript.Context; -import org.mozilla.javascript.ContextFactory; -import org.mozilla.javascript.ScriptableObject; - -import org.springframework.mock.web.test.MockHttpServletRequest; -import org.springframework.mock.web.test.MockHttpServletResponse; -import org.springframework.ui.ModelMap; -import org.springframework.validation.BindingResult; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -/** - * @author Jeremy Grelle - * @author Arjen Poutsma - */ -public class MappingJacksonJsonViewTests { - - private MappingJacksonJsonView view; - - private MockHttpServletRequest request; - - private MockHttpServletResponse response; - - private Context jsContext; - - private ScriptableObject jsScope; - - - @Before - public void setUp() { - request = new MockHttpServletRequest(); - response = new MockHttpServletResponse(); - jsContext = ContextFactory.getGlobal().enterContext(); - jsScope = jsContext.initStandardObjects(); - view = new MappingJacksonJsonView(); - } - - - @Test - public void isExposePathVars() { - assertEquals("Must not expose path variables", false, view.isExposePathVariables()); - } - - @Test - public void renderSimpleMap() throws Exception { - Map model = new HashMap(); - model.put("bindingResult", mock(BindingResult.class, "binding_result")); - model.put("foo", "bar"); - - view.setUpdateContentLength(true); - view.render(model, request, response); - - assertEquals("no-cache", response.getHeader("Pragma")); - assertEquals("no-cache, no-store, max-age=0", response.getHeader("Cache-Control")); - assertNotNull(response.getHeader("Expires")); - assertEquals(MappingJacksonJsonView.DEFAULT_CONTENT_TYPE, response.getContentType()); - - String jsonResult = response.getContentAsString(); - assertTrue(jsonResult.length() > 0); - assertEquals(jsonResult.length(), response.getContentLength()); - validateResult(); - } - - @Test - public void renderCaching() throws Exception { - view.setDisableCaching(false); - - Map model = new HashMap(); - model.put("bindingResult", mock(BindingResult.class, "binding_result")); - model.put("foo", "bar"); - view.render(model, request, response); - - assertNull(response.getHeader("Pragma")); - assertNull(response.getHeader("Cache-Control")); - assertNull(response.getHeader("Expires")); - } - - @Test - public void renderSimpleMapPrefixed() throws Exception { - view.setPrefixJson(true); - renderSimpleMap(); - } - - @Test - public void renderSimpleBean() throws Exception { - Object bean = new TestBeanSimple(); - Map model = new HashMap(); - model.put("bindingResult", mock(BindingResult.class, "binding_result")); - model.put("foo", bean); - view.setUpdateContentLength(true); - view.render(model, request, response); - - assertTrue(response.getContentAsString().length() > 0); - assertEquals(response.getContentAsString().length(), response.getContentLength()); - validateResult(); - } - - @Test - public void renderWithPrettyPrint() throws Exception { - ModelMap model = new ModelMap("foo", new TestBeanSimple()); - view.setPrettyPrint(true); - view.render(model, request, response); - - String result = response.getContentAsString().replace("\r\n", "\n"); - assertTrue("Pretty printing not applied:\n" + result, result.startsWith("{\n \"foo\" : {\n ")); - validateResult(); - } - - @Test - public void renderSimpleBeanPrefixed() throws Exception { - view.setPrefixJson(true); - renderSimpleBean(); - assertTrue(response.getContentAsString().startsWith("{} && ")); - } - - @Test - public void renderSimpleBeanNotPrefixed() throws Exception { - view.setPrefixJson(false); - renderSimpleBean(); - assertFalse(response.getContentAsString().startsWith("{} && ")); - } - - @Test - public void renderWithCustomSerializerLocatedByAnnotation() throws Exception { - Object bean = new TestBeanSimpleAnnotated(); - Map model = new HashMap(); - model.put("foo", bean); - view.render(model, request, response); - - assertTrue(response.getContentAsString().length() > 0); - assertEquals("{\"foo\":{\"testBeanSimple\":\"custom\"}}", response.getContentAsString()); - validateResult(); - } - - @Test - public void renderWithCustomSerializerLocatedByFactory() throws Exception { - SerializerFactory factory = new DelegatingSerializerFactory(null); - ObjectMapper mapper = new ObjectMapper(); - mapper.setSerializerFactory(factory); - view.setObjectMapper(mapper); - - Object bean = new TestBeanSimple(); - Map model = new HashMap(); - model.put("foo", bean); - model.put("bar", new TestChildBean()); - view.render(model, request, response); - - String result = response.getContentAsString(); - assertTrue(result.length() > 0); - assertTrue(result.contains("\"foo\":{\"testBeanSimple\":\"custom\"}")); - validateResult(); - } - - @Test - public void renderOnlyIncludedAttributes() throws Exception { - Set attrs = new HashSet(); - attrs.add("foo"); - attrs.add("baz"); - attrs.add("nil"); - - view.setModelKeys(attrs); - Map model = new HashMap(); - model.put("foo", "foo"); - model.put("bar", "bar"); - model.put("baz", "baz"); - view.render(model, request, response); - - String result = response.getContentAsString(); - assertTrue(result.length() > 0); - assertTrue(result.contains("\"foo\":\"foo\"")); - assertTrue(result.contains("\"baz\":\"baz\"")); - validateResult(); - } - - @Test - public void filterSingleKeyModel() throws Exception { - view.setExtractValueFromSingleKeyModel(true); - - Map model = new HashMap(); - TestBeanSimple bean = new TestBeanSimple(); - model.put("foo", bean); - Object actual = view.filterModel(model); - - assertSame(bean, actual); - } - - @SuppressWarnings("rawtypes") - @Test - public void filterTwoKeyModel() throws Exception { - view.setExtractValueFromSingleKeyModel(true); - - Map model = new HashMap(); - TestBeanSimple bean1 = new TestBeanSimple(); - TestBeanSimple bean2 = new TestBeanSimple(); - model.put("foo1", bean1); - model.put("foo2", bean2); - - Object actual = view.filterModel(model); - - assertTrue(actual instanceof Map); - assertSame(bean1, ((Map) actual).get("foo1")); - assertSame(bean2, ((Map) actual).get("foo2")); - } - - private void validateResult() throws Exception { - Object jsResult = - jsContext.evaluateString(jsScope, "(" + response.getContentAsString() + ")", "JSON Stream", 1, null); - assertNotNull("Json Result did not eval as valid JavaScript", jsResult); - } - - - public static class TestBeanSimple { - - private String value = "foo"; - - private boolean test = false; - - private long number = 42; - - private TestChildBean child = new TestChildBean(); - - public String getValue() { - return value; - } - - public boolean getTest() { - return test; - } - - public long getNumber() { - return number; - } - - public Date getNow() { - return new Date(); - } - - public TestChildBean getChild() { - return child; - } - } - - - @JsonSerialize(using = TestBeanSimpleSerializer.class) - public static class TestBeanSimpleAnnotated extends TestBeanSimple { - } - - - public static class TestChildBean { - - private String value = "bar"; - - private String baz = null; - - private TestBeanSimple parent = null; - - public String getValue() { - return value; - } - - public String getBaz() { - return baz; - } - - public TestBeanSimple getParent() { - return parent; - } - - public void setParent(TestBeanSimple parent) { - this.parent = parent; - } - } - - - public static class TestBeanSimpleSerializer extends JsonSerializer { - - @Override - public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeStartObject(); - jgen.writeFieldName("testBeanSimple"); - jgen.writeString("custom"); - jgen.writeEndObject(); - - } - } - - - public static class DelegatingSerializerFactory extends BeanSerializerFactory { - - public DelegatingSerializerFactory(Config config) { - super(config); - } - - @Override - public JsonSerializer createSerializer(SerializationConfig config, JavaType baseType, BeanProperty property) - throws JsonMappingException { - - if (baseType.getRawClass() == TestBeanSimple.class) { - return new TestBeanSimpleSerializer(); - } - else { - return super.createSerializer(config, baseType, property); - } - } - } - -}