{"id":210,"date":"2013-04-15T20:03:03","date_gmt":"2013-04-15T20:03:03","guid":{"rendered":"\/blog\/?p=210"},"modified":"2024-04-25T14:34:13","modified_gmt":"2024-04-25T14:34:13","slug":"using-spring-and-quartz-with-jobstore-properties","status":"publish","type":"post","link":"\/blog\/2013\/04\/using-spring-and-quartz-with-jobstore-properties\/","title":{"rendered":"Using Spring and Quartz with JobStore properties"},"content":{"rendered":"\n<p>This article describes how to use the Spring Framework and Quartz together when using <code>org.quartz.jobStore.useProperties=true<\/code>, meaning that all Job data is stored in the database as properties instead of serialized Java objects.<\/p>\n\n\n\n<p>Normally this is not possible, because the Spring class <code>SimpleTriggerFactoryBean<\/code> stores a reference to the <code>JobDetail<\/code> in the <code>JobDataMap<\/code>, which cannot be represented as a set of properties.<\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">Frameworks and versions<\/h2>\n\n\n\n<p>We\u2019ve used the following frameworks and versions. Maybe future versions fix the issue, making our work-around obsolete.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table  class=\" table table-hover\" ><thead><tr><th>Framework<\/th><th>Version<\/th><th>Description<\/th><\/tr><\/thead><tbody><tr><td>Spring Framework<\/td><td>3.1.2.RELEASE<\/td><td>DI framework<\/td><\/tr><tr><td>Quartz<\/td><td>2.1.7<\/td><td>Scheduling framework<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>See also&nbsp;<a href=\"https:\/\/web.archive.org\/web\/20150130124759\/http:\/\/www.quartz-scheduler.org\/documentation\/quartz-2.1.x\/configuration\/ConfigJobStoreTX\">http:\/\/www.quartz-scheduler.org\/documentation\/quartz-2.1.x\/configuration\/ConfigJobStoreTX<\/a>&nbsp;for more details about the Quartz JobStore configuration.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why do we want to use&nbsp;<em>useProperties=true<\/em>?<\/h3>\n\n\n\n<p>The main reason to use <em><code>useProperties=true<\/code><\/em>&nbsp;is to prevent storing serialized Java objects in the database, when we need to store additional information with Quartz jobs.<\/p>\n\n\n\n<p>First of all this makes the entries easier to read in a non-Java context, e.g. when examining the database contents with a DB-tool, or when dealing with SQL scripts.<\/p>\n\n\n\n<p>Second, this prevents us from having to deal with changing Java classes, leading to horrifying serialization exceptions (imaging having multiple incompatible class versions serialized in a database).<\/p>\n\n\n\n<p>Of course if serialization is properly setup, and is backwards compatible with older class versions, the problem might not be so significant, but we think using properties whenever possible reduces the risk for such errors.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Sample configuration<\/h2>\n\n\n\n<p>The following sample Spring configuration shows a typical Quartz setup that we want to use (but without our work-around). Please note that only settings relevant for this article are shown.<\/p>\n\n\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&lt;bean id=&quot;SampleScheduler&quot;&gt;\n\t&lt;property name=&quot;quartzProperties&quot;&gt;\n\t\t&lt;props&gt;\n\t\t\t&lt;prop key=&quot;org.quartz.jobStore.useProperties&quot;&gt;true&lt;\/prop&gt;\n\t\t&lt;\/props&gt;\n\t&lt;\/property&gt;\n\t&lt;property name=&quot;triggers&quot;&gt;\n\t\t&lt;list&gt;\n\t\t\t&lt;ref bean=&quot;SampleJob.trigger&quot;\/&gt;\n\t\t&lt;\/list&gt;\n\t&lt;\/property&gt;\n\t&lt;property name=&quot;jobDetails&quot;&gt;\n\t\t&lt;list&gt;\n\t\t\t&lt;ref bean=&quot;SampleJob&quot;\/&gt;\n\t\t&lt;\/list&gt;\n\t&lt;\/property&gt;\n&lt;\/bean&gt;\n&lt;bean id=&quot;SampleJob&quot;&gt;\n\t&lt;property name=&quot;jobClass&quot; value=&quot;com.trimplement.sample.SampleJob&quot; \/&gt;\n\t&lt;property name=&quot;durability&quot; value=&quot;true&quot; \/&gt;\n&lt;\/bean&gt;\n&lt;bean id=&quot;SampleJob.trigger&quot;&gt;\n\t&lt;property name=&quot;jobDetail&quot; ref=&quot;SampleJob&quot; \/&gt;\n\t&lt;property name=&quot;repeatInterval&quot; value=&quot;120000&quot; \/&gt;\n&lt;\/bean&gt;\n<\/pre>\n\n\n<p>If we were to start our application using this configuration with the default beans, we would get the following exception:<\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\njava.io.IOException: JobDataMap values must be Strings when the &#039;useProperties&#039; property is set.  Key of offending value: jobDetail\n        at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.convertToProperty(StdJDBCDelegate.java:3113)\n        at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.serializeProperties(StdJDBCDelegate.java:3080)\n        at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.serializeJobData(StdJDBCDelegate.java:3032)\n        at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.insertTrigger(StdJDBCDelegate.java:1052)\n        at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeTrigger(JobStoreSupport.java:1209)\n<\/pre>\n\n\n<p>This exception is caused by the fact that <code>SimpleTriggerFactoryBean<\/code> stores a reference to the <code>JobDetail<\/code> in the <code>JobDataMap<\/code>. The <code>JobDetail<\/code> cannot be represented as a property, hence it is refused by Quartz.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Solution<\/h2>\n\n\n\n<p>Our work-around is to remove the <code>JobDetail<\/code> reference from the <code>JobDataMap<\/code> before returning the created <code>SimpleTrigger<\/code>. For this we subclass the <code>SimpleTriggerFactoryBean<\/code> and remove it, before returning the Trigger.<\/p>\n\n\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\npublic class CustomSimpleTriggerFactoryBean extends SimpleTriggerFactoryBean {\n\t@Override\n\tpublic void afterPropertiesSet() throws ParseException {\n\t\tsuper.afterPropertiesSet();\n\t\t\/\/ Remove the JobDetail element\n\t\tgetJobDataMap().remove(JobDetailAwareTrigger.JOB_DETAIL_KEY);\n\t}\n}\n<\/pre>\n\n\n<p>Of course we need to reference our class in the Spring configuration.<\/p>\n\n\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&lt;bean id=&quot;SampleJob.trigger&quot; class=&quot;com.trimplement.sample.CustomSimpleTriggerFactoryBean&quot;&gt;\n    &lt;property name=&quot;jobDetail&quot; ref=&quot;SampleJob&quot; \/&gt;\n    &lt;property name=&quot;repeatInterval&quot; value=&quot;120000&quot; \/&gt;\n&lt;\/bean&gt;\n<\/pre>\n\n\n<p>When we start our application, the Job and Trigger are successfully created and persisted in the database.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>We presented a work-around for using Spring and Quartz together when we want to store Job data as properties in the database.<\/p>\n\n\n\n<p>This solution is fine as long as all Job data can be stored as properties instead of serialized Java objects. In general storing serialized Java objects in a database should be avoided, for obvious reasons: it requires a Java context to read\/update\/write such objects, and changing Java classes might break deserialization of existing persisted objects.<\/p>\n\n\n\n<p>Instead a proper ORM-strategy such as JPA should be used to store non-trivial data structures; triggers and jobs may store IDs to such objects as simple properties.<\/p>\n\n\n\n<p><br><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This article describes how to use the Spring Framework and Quartz together when using org.quartz.jobStore.useProperties=true, meaning that all Job data is stored in the database as properties instead of serialized Java objects. Normally this is not possible, because the Spring class SimpleTriggerFactoryBean stores a reference to the JobDetail in the JobDataMap, which cannot be represented [&hellip;]<\/p>\n","protected":false},"author":5,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[162],"tags":[89,88],"class_list":["post-210","post","type-post","status-publish","format-standard","hentry","category-software-development-tutorials","tag-jobstore","tag-spring-framework"],"_links":{"self":[{"href":"\/blog\/wp-json\/wp\/v2\/posts\/210","targetHints":{"allow":["GET"]}}],"collection":[{"href":"\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"\/blog\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"\/blog\/wp-json\/wp\/v2\/comments?post=210"}],"version-history":[{"count":13,"href":"\/blog\/wp-json\/wp\/v2\/posts\/210\/revisions"}],"predecessor-version":[{"id":2520,"href":"\/blog\/wp-json\/wp\/v2\/posts\/210\/revisions\/2520"}],"wp:attachment":[{"href":"\/blog\/wp-json\/wp\/v2\/media?parent=210"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"\/blog\/wp-json\/wp\/v2\/categories?post=210"},{"taxonomy":"post_tag","embeddable":true,"href":"\/blog\/wp-json\/wp\/v2\/tags?post=210"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}