quartz jboss spring multiple webapps - spring

I have been successfully using quartz in my application.
Basically I have quartz bundled inside the webapp1 which is running inside the Jboss.
But we have got another webapp2 running in the jboss which needs to have quartz job as well
Now what I need to do is to have quartz scheduler running in the jboss as some kind of service and both the webapps should be able
to register their jobs on the single quartz scheduler.
below is my related spring configuration for webapp1 which has beenworking till now.
<bean id="qtzScheduler"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource">
<ref bean="jndiDataSource" />
</property>
<property name="applicationContextSchedulerContextKey">
<value>applicationContext</value>
</property>
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="schedulerName" value="webapp1" />
</bean>
<bean id="wrapperScheduler" class="uk.fa.quartz.schedule.ServiceScheduler">
<property name="scheduler">
<ref bean="qtzScheduler" />
</property>
</bean>
<bean id="jndiDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:/FmManagerDS</value>
</property>
</bean>
when I have to schedule the job,code is like below:
WrapperScheduler scheduler = (WrapperScheduler) ctx.getBean("wrapperScheduler");
scheduler.scheduleCronJob(job, jobName + "CronTrigger", WrapperScheduler.TRIGGER_GROUP, cronExpression);
Now I dont want to define the same scheduler again in webapp2 which will cause 2 quartz scheduler running in the jboss.
Can someone has any idea how to do it ?
I saw one example on the internet like below Link which I think is doing what I want.
But I dont understand how I can integrate this with my system using the datasource defined in my spring source.
If anybody can share the configuration or point me to the right resource on internet,I would be highly thankful.

The link you refer to explains how to access Quartz scheduler services built into JBoss. I have never used such approach but basically you let JBoss handle your scheduler, data source and everything around it. This makes it very easy to take advantage of job scheduling without all the configuration hustle - but is not very flexible and your application is no longer self-contained.
In your case I see two options worth investigating:
Clustered Quartz scheduler
Configure both of your web applications to run in a cluster. Both applications will share the same database and will run jobs defined in each other. This might not be an option for you due to several reasons:
both applications must be able to run jobs defined by each other - e.g. job classes must be available on CLASSPATH
you still need to define Quartz configuration in both applications (you can easily share configuration though, e.g. by extracting the XML configuration into a separate file)
both applications will maintain separate thread pool
Clustering is more suited for homogeneous applications running on several machines, not heterogeneous ones on single node.
Remote scheduler
Quartz has a built-in support for remote schedulers via rmi. Basically one application hosts full-blown Quartz server whilst the other connects to that server. This seems like a better approach for you (let's call it "master-slave") as only one application manages the scheduler while the other uses existing one.
See: RemoteScheduler.

Finally I got time to write it all down for all others who might have to use Quartz as a service running in jboss.
But other options as mentioned in his answer by #Tomasz can also be tried.
Please note that you will get a null reference back if you try to retrieve it from outside the JBoss server in which it was bound. In case you have such a requirement you might want to consider using Quartz's RMI support instead.
1)First of all ensure that you remove any existing versions of the quartz in the jboss/[profile]/lib or the quartz.rar which is comes up with the jboss distribution.
2)Please your quartz.1.8.3.jar & quartz-jboss.1.8.jar into the acccesmanager/[profile]/lib
3)Below is the code for quartz-service.xml which needs to be placed in the jboss deploy folder which will start the Quartz scheduler:
<server>
<mbean code="org.quartz.ee.jmx.jboss.QuartzService"
name="user:service=QuartzService,name=QuartzService">
<attribute name="JndiName">Quartz</attribute>
<attribute name="Properties">
org.quartz.scheduler.instanceName = BGSScheduler
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.xaTransacted = false
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 5
org.quartz.threadPool.threadPriority = 4
org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer = true
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreCMT
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource = QUARTZ
org.quartz.dataSource.QUARTZ.jndiURL = java:FmManagerDS
org.quartz.jobStore.nonManagedTXDataSource = QUARTZ_NO_TX
org.quartz.dataSource.QUARTZ_NO_TX.jndiURL = java:FmManagerDS
</attribute>
<depends>jboss.jca:service=DataSourceBinding,name=FmManagerDS</depends>
</mbean>
</server>
]
Most of things are self explanatory or you can get more details on this at Quartz Configuration
The key thing is to note that quartz requires 2 datasources.One is the container managed datasource-same one as one defined in jboss *-ds.xml(java:FmManagerDS in my case).
If your 'org.quartz.jobStore.dataSource' is XA, then set 'org.quartz.jobStore.nonManagedTXDataSource' to a non-XA datasource (for the same DB). Otherwise, you can set them to be the same.
Then in spring applicationContext,I had to get the handle of quartz so that I could inject into the wrapperScheduler.Code for that is below
<bean id="quartzScheduler" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>Quartz</value>
</property>
</bean>
<bean id="wrapperScheduler" class="k.fa.quartz.schedule.ServiceScheduler">
<property name="scheduler">
<ref bean="quartzScheduler" />
</property>
</bean>
Then we can schedule the jobs using below
Timestamp t = new Timestamp (System.currentTimeMillis());
ScheduleJob job = new ScheduleJob(EmailJob.class.getCanonicalName() +t.toString(), EmailJob.class);
Below is the code to pass the spring applicationContext to the EmailJob so that we can access beans and other things.
To achieve that we need to implement the ApplicationContextAware interface so that applicationContext is available and same is then
pushed into the schedulerContext.Please ensure that we dont put applicationContext into the JobdataMap incase you are using JDBC store as it gives serialization issues.
serviceScheduler.getScheduler().getContext().put("applicationContext", ctx);
serviceScheduler.scheduleCronJob(job, "test" + t.toString(), ServiceScheduler.DEFAULT_TRIGGER_GROUP, cronExpression);
Others who are not using wrapperscheduler can similarly get the handle of the quartz directly into their code using below
InitialContext ctx = new InitialContext();
Scheduler scheduler = (Scheduler) ctx.lookup("Quartz");
ScheduleJob job = new ScheduleJob(EmailJob.class.getCanonicalName() +t.toString(), Executor.class);
scheduler.scheduleJob(job, trigger);
In the Email job class you can use below to get the applicationContext
applicationContext = (ApplicationContext) context.getScheduler().getContext().get(APPLICATION_CONTEXT_KEY);
//get spring bean and do the necessary stuff
Another important thing is that as the quartz schedler is running outside the webapplication,quartz wont be able to fire the jobclass if it is inside the war.It needs to be in the shared jar in the jboss/[profile]/lib.

Related

Programatically set v$session program property

I found some answers on the problem but none that i could make work in my case. My problem is that I load a datasource from my JBoss configuration with spring:
<xa-datasource jndi-name="java:jboss/jdbc/oracleDatasource" pool-name="jdbc/oracleDatasource" enabled="true">
<xa-datasource-property name="URL">
jdbc:oracle:thin:#URL:1522:SID
</xa-datasource-property>
<xa-datasource-property name="connectionProperties">
v$session.program=MyAPP
</xa-datasource-property>
<driver>oracle-jdbc</driver>
The spring loading is made as follows:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:jboss/jdbc/oracleDatasource"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="dataSource"/>
</bean>
As you can see, I have set the v$session.program property in JBoss, it works well.
The problem is that i have several applications (war) that can be deployed on the same JBoss server, using this configuration. What I want to do in this case is to have each of my application to have its own name written in the v$session.program property.
So basically, i would like to be able to load the same datasource on each app but to have each of them using its own name to log the program property in oracle DB. Is it possible or do I have to have one datasource for each application hosted?
The only thing you need it to intercept each call of getConnection from connection pool.
You must obtain a real Oracle connection - not a proxy - and call the setClientInfo on 12c or setEndToEndMetrics in older versions to set the action / client / module identification.
An example see here.
Also note that the use of dbms_application_info for this same purpose works as well, but this produces a one server roundtrip too much. The setClientInfo doesn't produce server call, but stores this information for the next statement execution (which is the preformance saving approach).
Also note that to use this feature, your driver must match perfectly with your database - the strange exeptions you can see while setting teh client info are in most cases caused by the incompatibility of teh JDBC driver and the RDBMS.
If putting this information into v$session.module or v$session.client_info is an option, you can do using Java code.
All you need to do is call dbms_application_info.set_module() or dbms_application_info.set_client_info() after your Java code obtained the connection from the datasource.
Something like this:
Connection conn = ... // get connection from the DataSource
CallableStatement cstmt = conn.prepareCall("{call dbms_application_info.set_client_info(?)}");
cstmt.setString(1, "Some interesting information");
cstmt.execute();

spring batch vs quartz jobs?

I am new to batch processing. I am trying to start with simple scheduler and job. But i am confused b/w
spring batch vs quartz jobs. My understanding is
Quartz :- quartz provides both frameworks i.e scheduler framework and job framework(in case I do not want to use spring batch jobs). Right ?
Spring Batch :- It only provides the job framework . I have always send using Quatz schecduler to schedule spring batch jobs.
Does spring provides its own scheduler also ?
Quartz is a scheduling framework. Like "execute something every hour or every last Friday of the month"
Spring Batch is a framework that defines that "something" that will be executed.
You can define a job, that consists of steps. Usually, a step is something that consists of an item reader, an optional item processor, and an item writer, but you can define a custom step. You can also tell Spring Batch to commit every 10 items and a lot of other stuff.
You can use Quartz to start Spring Batch jobs.
So basically Spring Batch defines what should be done, and Quartz defines when it should be done.
There is answer for this question in official FAQ
How does Spring Batch differ from Quartz?
Is there a place for them both in a solution?
Spring Batch and Quartz have different goals. Spring Batch provides functionality for processing large volumes of data and Quartz provides functionality for scheduling tasks. So Quartz could complement Spring Batch, but are not excluding technologies. A common combination would be to use Quartz as a trigger for a Spring Batch job using a Cron expression and the Spring Core convenience SchedulerFactoryBean.
Does spring provides its own scheduler also?
Yes, using Spring TaskScheduler as follows:
<task:scheduled-tasks>
<task:scheduled ref="runScheduler" method="run" fixed-delay="5000" />
</task:scheduled-tasks>
<task:scheduled-tasks>
<task:scheduled ref="runScheduler" method="run" cron="*/5 * * * * *" />
</task:scheduled-tasks>
full example
With Quartz Scheduler as follows:
<!-- run every 10 seconds -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="jobDetail" />
<property name="cronExpression" value="*/10 * * * * ?" />
</bean>
</property>
</bean>
full example
Spring Batch: reads data from a datasource (table in a database, flat file, etc), processes that data. Then stores the data in another datasource and may be in another format.
I have made a tutorial in my blog on how to integrate Spring Boot 2, Spring batch and Quartz.
You can integrate Spring boot and spring batch and skip the Quartz integration.
Quartz is a scheduler that schedules a task in the future and it has its own metadata tables to manage the state of the jobs.

Spring+quartz should I make my own bean?

I need a job that can run every 1 minutes betwen 17h and 18h, it should not be relaunched if the job is unfinished.
The org.springframework.scheduling.quartz.CronTriggerBean seems to be what I need but I found nothing about concurrency.
Would you know a quartz bean which would fit my needs?
Every javadoc I found has almost all its link broken.
http://docs.spring.io/spring/docs/3.1.x/javadoc-api/org/springframework/scheduling/quartz/CronTriggerBean.html
Or will I have to make my own kind of bean?
quartz is in 1.8.5 and spring in 2.5.6
Thanks.
-Sure, the CronTriggerBean is suitable for your case. The expression you need is 0 * 17 * * ? and will run for every minute starting at 17.00 with the last trigger happening at 17.59.
-In order to disable concurrency, in newer versions you can put #DisallowConcurrentExecution over your job class. In 1.8 version I think that annotation is not supported, and instead you need to put "implements StatefulJob" in your job class so that it implements StatefulJob that can be run only by one thread a time
-a sample app using quartz 1.8 can be found at http://www.mkyong.com/spring/spring-quartz-scheduler-example/
The 2.5 JavaDoc can be found here.
In Spring 2.5 you can set a concurrent attribute in the XML when using MethodInvokingJobDetailFactoryBean. Setting it prevents multiple instances from running at the same time, but it should be noted that triggers will be queued up and launched when the previous instance of the job finishes.
Here is a sample:
<bean id="fooJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="fooManager" />
<property name="targetMethod" value="myJOb" />
<property name="concurrent" value="true"/>
</bean>

How to set up Eclipselink cache coordination with Wildfly

I have set up a persistancce caching with Eclipselink on Wildfly 8. It works, but I also want to do cache coordination. I have the following setup for Eclipselink cache coordination in my persistance.xml:
<property name="eclipselink.cache.coordination.protocol" value="jms" />
<property name="eclipselink.cache.coordination.jms.topic" value="jms/MemberTopic" />
<property name="eclipselink.cache.coordination.jms.factory" value="jms/MemberConnectionFactory" />
However, when my entity is merged, no messages are sent by Eclipselink. I have logging set to "ALL", but nothing appears in the console.
I tried adding coordinationType=CacheCoordinationType.SEND_NEW_OBJECTS_WITH_CHANGES to entity's #Cache annotation, but it doesn't change anything. Also tried using an MDB as suggested for WebSphere (http://www.eclipse.org/eclipselink/documentation/2.4/concepts/cache011.htm#CDECEHFH).
The JMS topic and connectionfactory exist and Wildfly startup / application deployment shows no errors. For server clustering I run Wildfly in domain mode.
The problem ironically was in my Wildfly configuration instead - I didn't have my messaging cluster set up. I used the default messaging cluster settings from full-ha profile and set Eclipselink's cache coordination host accordingly:
<property name="eclipselink.cache.coordination.jms.host" value="231.7.7.7:9876" />

Where to store database passwords in a spring application or use JNDI?

Seems like a simple task. I have a webapp which requires a database connection. I'd like to be able to drop an updated .war file on this app server and load a new version without having to re-edit an applicationConfig.xml file to specify the database connection parameters for production.
Is using the container to setup the data source and then referencing it from JNDI the preferred way to go? I think it is cleaner having it all defined in the spring .xml file, but I can't come up with a clean way to allow the production password to be set only once as we roll out new versions.
So, how do you specify your database connection information in a spring application so that you can upgrade it without having to re-edit the files?
If you use JNDI, how do you handle setting up of your tests since the JNDI is not going to be available outside of the container?
Thanks!
The typical way to externalize database connection properties is to store them in a .properties file and load using <context:property-placeholder .../> . Then you can have different .properties files for testing and production.
If you choose JNDI, you can use a Spring's mock JNDI support for testing.
One approach is for your Spring configuration file to be composed of fragments related to specific layers in your application.
One such fragment could contain your DataSource defintion. For production, this fragment would use a jee:jndi-lookup. And then for test, have a different fragment would use a DriverManagerDataSource ?
Update:
If you want to change the datasource after deployment, then you can use this technique, along with changing the which datasource is injected into your other beans using a PropertyPlaceholderConfigurer as explained in an old post I wrote
eg:
<bean class="foo.bar.SomeClassNeedingDataSource"">
<property name="dataSource" ref="${the.datasource.to.inject}" />
</bean>
<jee:jndi-lookup id="jndiDataSource" ... />
<bean id="driverManagerDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
...
</bean>
# the properties file
the.datasource.to.inject = jndiDataSource
#the.datasource.to.inject = driverManagerDataSource

Resources