We are using Quartz 2.1.5; we have the following properties set:
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.CloudscapeDelegate
org.quartz.jobStore.useProperties = true
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.jobStore.isClustered=true
org.quartz.jobStore.clusterCheckinInterval=20000
and the following beans configuration:
<bean name="abcRequestsJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.hsc.correspondence.job.AbcRequestsJob" />
<property name="group" value="sftpTransfers"/>
</bean>
<bean id="abcRequestsJobTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="abcRequestsJob" />
<property name="group" value="sftpTransfers"/>
<property name="cronExpression" value="${quartz.abcRequests.cronExpression}" />
</bean>
When we run, we are getting an error saying that
nested exception is org.quartz.JobPersistenceException: Couldn't store trigger 'sftpTransfers.abcRequestsJobTrigger' for 'sftpTransfers.abcRequestsJob'
job:JobDataMap values must be Strings when the 'useProperties' property is set.
Key of offending value: jobDetail
[See nested exception: java.io.IOException: JobDataMap values must be Strings when the 'useProperties' property is set. Key of offending value: jobDetail]
Is there another way to configure a CronTriggerFactoryBean than using a reference to the JobDetailFactoryBean reference, or a different trigger factory bean that only takes strings as properties? This all worked before we wanted to use clustering, but now that the job is going to be written to a blob they want only strings to be persisted. That's fine, how do I get it done?
Please refer:
http://site.trimplement.com/using-spring-and-quartz-with-jobstore-properties/
http://forum.springsource.org/archive/index.php/t-130984.html
Problem:
This happens with 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.
Error is because of Spring class CronTriggerFactoryBean that stores a reference to the JobDetail in the JobDataMap, which cannot be represented as a set of properties.
CronTriggerFactoryBean is setting the jobDetail into the trigger's jobDataMap.
Workaround:
Extend CronTriggerFactoryBean and remove JobDetail from jobDataMap.
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailAwareTrigger;
public class PersistableCronTriggerFactoryBean extends CronTriggerFactoryBean {
#Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
//Remove the JobDetail element
getJobDataMap().remove(JobDetailAwareTrigger.JOB_DETAIL_KEY);
}
}
Related
I am using spring batch, but due to job instance already exist error I need to add current time in my job parameter. I am unable to figure out where to add job parameters. Here is my code:
<step id="myStep">
<tasklet>
<chunk reader="myReader" processor="myProcessor" writer="myWriter" commit-interval="6000" skip-limit="9000">
//some more code.
</chunk>
</tasklet>
</step>
<bean id="myReader" class="org.springframework,batch.item.database.StoredProcedueItemReader" scope="step">
//define property for datasource , procedurename , rowmapper, parameters
<property name="preparedStatementSetter" ref="myPreparedStatmentSetter">
</bean>
<bean id="myPreparedStatmentSetter" class="com.mypackage.MyPreparedStatementSetter" scope="step">
<property name="kId" value="#{jobParameters[kId]}">
</bean>
When I try to run the job for same kId multiple times I get The job already exist error, so I need to add current timestamp to my job parameter.
Would adding current time stamp as a property in the bean myPreparedStatmentSetter be sufficient, or do I need to add jobparameter somewhere else too? From where exactly are jobparameters picked from in spring file?
In case I need to add timestamp to the bean here is a questions -My stored procedure takes only kID as paramter, I dont need to pass current time stamp to stored procedure, then why I need to add the same in myPreparedStatmentSetter.
Also how would I add current timestamp in an xml file without java code?
EDIT
Here is my jobLauncher bean
<bean Id= "jobLauncher "class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" value="myJobRepo">
</bean>
Adding a "random" job parameter by hand, while it can work, isn't the most ideal way to get around the job instance already exists error. Instead, you should consider adding a JobParametersIncrementer to your job. Spring provides the RunIdIncrementer as an implementation of this out of the box. A job configured with it would look something like the following:
#Bean
public Job myJob() {
return jobBuilderFactory.get("myJob")
.incrementer(runIdIncrementer())
.start(step1())
.build();
}
#Bean
public JobParametersIncrementer runIdIncrementer() {
return new RunIdIncrementer();
}
I am guessing that you already adding KId to your job parameters. Add following to your joblaucher.run() method.
new JobParametersBuilder()
.addLong("time",System.currentTimeMillis())
.addLong("KId",<your KID>)
.toJobParameters();
According to this answer, you can use the Spring Batch class org.springframework.batch.support.SystemPropertyInitializer to set a System Property during startup of a Spring Context.
In particular, I was hoping to be able to use it to set ENVIRONMENT because part of Spring Batch config reads:
<bean id="placeholderProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:/org/springframework/batch/admin/bootstrap/batch.properties</value>
<value>classpath:batch-default.properties</value>
<value>classpath:batch-${ENVIRONMENT:hsql}.properties</value>
</list>
</property>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreResourceNotFound" value="true" />
<property name="ignoreUnresolvablePlaceholders" value="false" />
<property name="order" value="1" />
</bean>
But SystemPropertyInitializer uses afterPropertiesSet() to set the System Property, and apparently this happens after the configuration of PropertyPlaceholderConfigurer.
Is it possible to achieve this?
The easiest solution would be to pass in the environment property as a command-line argument, so it can be resolved as a system property.
If that's not an option you can implement a ApplicationContextInitializer that promotes environment properties to system properties.
public class EnvironmentPropertyInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext> {
boolean override = false; //change if you prefer envionment over command line args
#Override
public void initialize(final ConfigurableApplicationContext applicationContext) {
for (Entry<String, String> environmentProp : System.getenv().entrySet()) {
String key = environmentProp.getKey();
if (override || System.getProperty(key) == null) {
System.setProperty(key, environmentProp.getValue());
}
}
}
}
Here it looks like you're using Spring Batch Admin, so you can register your initializer with a slight addition to the web.xml file:
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>org.your.package.EnvironmentPropertyInitializer</param-value>
</context-param>
Adding Background Since a Comment Didn't Seem Sufficient: Here's the relevant classes and the order in which they are called/evaluated.
The ApplicationContextInitializer tells the Spring Application how to load an application context and can be used to set bean profiles, and change other aspects of the context. This gets executed before the context gets completely created.
The PropertyPlaceholderConfigurer is a BeanFactoryPostProcessor and calls postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory). This modifies the BeanFactory to allow for resolution of properties like ${my.property:some.default} when setting the properties of a bean as it is created by the BeanFactory.
The SystemPropertyInitializer implements InitializingBean and calls afterPropertiesSet(). This method runs after a bean is instantiated and the properties have been set.
So you're right that the SystemPropertyInitializer will not help here since it evaluates after the properties are set on the PropertyPlaceholderConfigurer. The ApplicationContextInitializer, however, will be able to promote those environment properties to system properties so they can be interpreted by the XML.
And one more note that I forgot to mention, one of the first declared beans will need to be:
<context:property-placeholder/>
Though it seems redundant, it will allow your PropertyPlaceholderConfigurer bean to evaluate ${ENVIRONMENT:hsql} correctly by using the environment properties you just promoted.
I am using annotation based scheduler like #Scheduled(fixedDelay = 1200000)
and I want to pass information like langId , loginUserId etc.
I have a scheduler MyScheduler, I have configured it in .property file as:
<task:annotation-driven />
<bean id="myScheduler" class="ab.abc.txn.service.MyScheduler"></bean>
use <property in bean defination
I have the following Spring bean for a remote web service defined in xml:
<bean id="authWSTemplate" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean" abstract="true">
<property name="serviceInterface" value="com.example.webservices.Authentication" />
<property name="wsdlDocumentUrl" value="${ws.root}/authentication?wsdl" />
<property name="namespaceUri" value="http://security.webservices.example.com/" />
<property name="serviceName" value="AuthenticationWebService" />
<property name="portName" value="AuthenticationPort" />
<property name="maintainSession" value="true" />
</bean>
How do I obtain this bean template and create a concrete bean (i.e. supply the root property)? Can I then put the concrete bean into the Spring container?
I need numerous concrete beans pointing to different systems, so I have different root values. For this example, say there are 2 systems with roots: http://domain1.com:8001/ws and http://domain2.com:8002/ws.
Therefore I'd want 2 beans called "authWSdom1" and "authWSdom2".
I'm expecting to do this programmatically in an application initialisation block, where I'd retrieve a list of all known system implementations (this info is only known at runtime), and create a bean for each impl, cache the bean name, then my application will retrieve the appropriate bean from the Spring container when required.
Or, is there a better pattern for this? Perhaps by providing the root value in a constructor for the bean?
I'm thinking I cannot have a single bean in Spring as I need to support concurrent access across multiple end points (i.e. multiple users hitting domain1 and domain2 at the same time).
Create custom bean that implements BeanFactoryPostProcessor and InitializingBean.
Use postProcessBeanFactory method to create bean:
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
String wsdlDocumentUrl = ....;
// .......
registry.registerBeanDefinition(YOUR_BEAN_NAME, BeanDefinitionBuilder.childBeanDefinition(
getParentNoDomainServicBeanName(authWSTemplate)).addPropertyReference(
"wsdlDocumentUrl", wsdlDocumentUrl).getBeanDefinition());
}
While I believe that Ragnor's answer is suitable if you want to dynamically create the bean in the spring container, I decided to use spring to define my own WSTemplate DTO then use a factory class to use this DTO and programmatically build (root url provided at runtime and DTO suffix value added to it) and cache the resulting JaxWS ProxyBean:
<bean id="authWSTemplate" class="com.example.WSProxyTemplate">
<property name="serviceInterface" value="com.example.webservices.Authentication" />
<property name="wsdlDocumentUrlSuffix" value="/authentication?wsdl" />
<property name="namespaceUri" value="http://security.webservices.example.com/" />
<property name="serviceName" value="AuthenticationWebService" />
<property name="portName" value="AuthenticationPort" />
<property name="maintainSession" value="true" />
</bean>
I like this approach as my spring config is abstracted away from the actual WS bean used. I.e. if I wanted to use something other that JaxWS, then I'd simply write a different factory which used the same DTO beans. Again, this would help if I have to choose the WS implementation at runtime depending upon some system/env criteria.
I need a quick help from you to fix a small problem.
In one of my project (using spring as core container), i am using ehcache to cache data. I am using spring ehcache annotations project (http://code.google.com/p/ehcache-spring-annotations/) for the same.
I want to have flexibility to enable and disable ehcache based on a external property. I read ehcache documentation and found that it reads system property net.sf.ehcache.disabled internally and if it set to true cache will be disabled and they recommend to pass this as -Dnet.sf.ehcache.disabled=true in the command line
I wanted to control it through externalized spring property file.
Then i thought of setting this system property in my spring application context file using MethodInvokingFactoryBean based on a externalized property.
Here is the code
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="java.lang.System"/>
<property name="targetMethod" value="getProperties"/>
</bean>
</property>
<property name="targetMethod" value="putAll"/>
<property name="arguments">
<util:properties>
<prop key="net.sf.ehcache.disabled">"${ehcache.disabled}"</prop>
</util:properties>
</property>
</bean>
Obviously ehcache.disabled is controlled via my externalized property file.
Plumbing works great but i guess order is not coming into place. By the time Application context is setting system property, cache is initialized and when cache was being initialized there was no property net.sf.ehcache.disabled, hence cache is not getting disabled. When i run application in debug mode and try to get System.getProperty("net.sf.ehcache.disabled") after application context is initialized, it is giving me the right value. (I tried both true and false).
One more thing i want to mentioned i am using spring wrapper to initialize my cache.
<ehcache:annotation-driven self-populating-cache-scope="method"/>
<ehcache:config cache-manager="cacheManager">
<ehcache:evict-expired-elements interval="20"/>
</ehcache:config>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
p:configLocation="classpath:ehcache.xml"/>
I believe that disabling cache can not be that hard.
What is it that i am missing? Is there any easy way to do it in spring except command line?
As of Spring 3.1 it is possible to use bean profiles which can read a property from a external property. You could wrap the declared bean inside a beans element:
<beans profile="cache-enabled">
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:configLocation="classpath:ehcache.xml"/>
</beans>
And then activate that using an external property as mentioned in this blog post.
The easiest way (pre-Spring 3.1) would be to add a bean id to your MethodInvokingFactoryBean declaration, then add a depends-on relationship.
You can return NoOpCacheManager object if your external property is not true
#Value("${cache.enabled}")
private Boolean cacheEnabled;
#Bean(destroyMethod="shutdown")
public net.sf.ehcache.CacheManager ehCacheManager() {
net.sf.ehcache.config.Configuration config = new net.sf.ehcache.config.Configuration();
CacheConfiguration cacheConfiguration = new CacheConfiguration();
cacheConfiguration.setName("mycache");
cacheConfiguration.setMemoryStoreEvictionPolicy("LRU");
cacheConfiguration.setMaxEntriesLocalHeap(1000);
cacheConfiguration.setTimeToIdleSeconds(0);
cacheConfiguration.setTimeToLiveSeconds(3600);
cacheConfiguration.persistence(new PersistenceConfiguration().strategy(PersistenceConfiguration.Strategy.NONE));
config.addCache(cacheConfiguration);
cacheMaxEntriesLocalHeap, cacheEvictionPolicy);
return net.sf.ehcache.CacheManager.newInstance(config);
}
#Bean
#Override
public CacheManager cacheManager() {
if(cacheEnabled) {
log.info("Cache is enabled");
return new EhCacheCacheManager(ehCacheManager());
}else{
log.info("Cache is disabled");
return new NoOpCacheManager();
}
}