Monday, April 30, 2012

Scheduling jobs with Quartz + Spring 3

You are asked to schedule a job to run under a certain frequency. How would you solve this requirement?

You can easily add Quartz to your project to meet this requirement. But using Quartz + Spring configuration in your project is a cleaner solution and more powerful.

In this example we will create a simple service that says "Hellooo World!!!!" every one minute using a cron expression. Then we will see how to start the schedule when you deploy your application in your server.

On the first place, let's see the pom file for the scheduler project:

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>

 <groupId>ar.com.pabloExample</groupId>
 <artifactId>spring-quartz-example</artifactId>
 <version>1.0-SNAPSHOT</version>
 <packaging>jar</packaging>

 <name>spring-quartz-example</name>
 <url>http://maven.apache.org</url>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>

 <repositories>
  <repository>
   <id>springsource-repo</id>
   <name>SpringSource Repository</name>
   <url>http://repo.springsource.org/release</url>
  </repository>
 </repositories>

 <dependencies>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>provided</scope>
  </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context-support</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>provided</scope>
  </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-tx</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>provided</scope>
  </dependency>

  <dependency>
   <groupId>org.springframework.batch</groupId>
   <artifactId>spring-batch-core</artifactId>
   <version>2.1.8.RELEASE</version>
   <scope>provided</scope>
  </dependency>

  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.8.2</version>
   <scope>test</scope>
  </dependency>
 </dependencies>
</project>

As this scheduler service project will be packaged as a JAR file, every dependency is in provided or test scope. This is because we will package it inside a WAR file that will have the same dependencies but in compile scope.

Now, this is the service to be scheduled:

HelloWorldService

package ar.com.pabloExample.service;

public class HelloWorldService {
 
 public void sayHelloWorld() {
  System.out.println("Hellooo World!!!!");
 }
}

Now, let's create a XML file where we will define the HelloWorldService as a Spring bean and the rest of scheduling configuration beans.

beans-spring.xml

<bean id="helloWorldService" class="ar.com.pabloExample.service.HelloWorldService" />

<bean id="myJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
 <property name="targetObject" ref="helloWorldService" />
 <property name="targetMethod" value="sayHelloWorld" />
</bean>

<bean id="everyMinuteTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
 <property name="jobDetail" ref="myJobDetail" />
 <property name="cronExpression" value="0 0/1 * 1/1 * ? *" />
</bean>

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
 <property name="jobDetails">
  <list>
   <ref bean="myJobDetail" />
  </list>
 </property>
</bean>

Let's see this XML in more detail:

<bean id="myJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
 <property name="targetObject" ref="helloWorldService" />
 <property name="targetMethod" value="sayHelloWorld" />
</bean>
This bean defines the class and method of that class that is going to be scheduled.


<bean id="everyMinuteTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
 <property name="jobDetail" ref="myJobDetail" />
 <property name="cronExpression" value="0 0/1 * 1/1 * ? *" />
</bean>
This bean defines a trigger associated to the previous bean. Here a cron expression is used, this one in particular defines a frequency of one minute. You can easily create cron expressions using online tools like this one.

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
 <property name="triggers">
  <list>
   <ref bean="everyMinuteTrigger" />
  </list>
 </property>
</bean>
This is the bean that can holds the list of jobs chosen to run. When this bean is instantiated, the scheduler starts. An explanation on how to start the scheduler is the next step to be explained.

The result up to now should be a jar file with the class to be scheduled and the bean configuration file.


I already have the schedule configuration, how should I start the schedule?


Ways to initiate the scheduling:
1- Only if using JBoss AS, you can use Snowdrop; it will automatically scan deployed beans and instantiate every bean under *spring.xml name pattern. This is pure server configuration, it is useful for deploying either a WAR or a simple JAR file. Take a look at this post to see the configuration needed, except for the EJB bean injection; you don't need that part.
2- Add a Spring listener in your web.xml that automatically deploys chosen XML files (you define the listener and the chosen bean files in the same web.xml file). Basically this is the same as the above way but within the context of a webapp.


Creating a webapp to deploy the scheduling service


For the most usual case, this is the configuration you should follow to schedule within the context of a webapp (a WAR file):

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 
 <groupId>ar.com.pabloExample</groupId>
 <artifactId>spring-quartz-web-example</artifactId>
 <packaging>war</packaging>
 <version>1.0-SNAPSHOT</version>
 
 <name>spring-quartz-web-example Maven Webapp</name>
 <url>http://maven.apache.org</url>

 <dependencies>

  <!-- This dependency has the ContextLoaderListener defined in web.xml -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>compile</scope>
  </dependency>

  <!-- This dependency has the beans-spring.xml with all the quartz config included -->

  <dependency>
   <groupId>ar.com.pabloExample</groupId>
   <artifactId>spring-quartz-example</artifactId>
   <version>1.0-SNAPSHOT</version>
   <scope>compile</scope>
  </dependency>

  <!-- In the war all the jars needed by spring-quartz-example to work the are included -->

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>compile</scope>
  </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context-support</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>compile</scope>
  </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-tx</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>compile</scope>
  </dependency>

  <dependency>
   <groupId>org.springframework.batch</groupId>
   <artifactId>spring-batch-core</artifactId>
   <version>2.1.8.RELEASE</version>
   <scope>compile</scope>
  </dependency>
 </dependencies>

 <build>
  <finalName>spring-quartz-web-example</finalName>
 </build>

</project>

For the WAR file, the pom has the dependency for the artifact spring-quartz-example which is the jar file that includes the HelloWorldService class and the beans configuration XML that was created in the previous step.

web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
 <display-name>Archetype Created Web Application</display-name>
 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:beans-spring.xml</param-value>
 </context-param>

 <listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>
</web-app>

This way, when you deploy your WAR file in the server, the Spring Context listener will scan your beans-spring.xml file and deploy the beans. Nothing else is needed at this point; the schedule has already been started.

Tuesday, April 17, 2012

Aspect Oriented Programming (AOP) with Spring 3

What does it mean?

AOP is a programming paradigm as well as Object Oriented Programming (OOP). In OOP we work with "objects" in a procedural world, meanwhile in AOP we work with "aspects" in a declarative world. You can use AOP concepts while working mainly with OOP, like the example presented below.


What's the basic concept behind?

The basic concept is to declare aspects. An aspect is a method of a class that will run automatically whenever certain conditions occur. Really simple but powerful.

It is similar to database triggers, you need to do something if some event occurs. The simplest example can be when you want to automatically log something before any method of your service layer runs.

Enough, let's see an example!


Hello World Spring 3 AOP example:

First of all we will create a service with a method that just does nothing (DummyService). Then, we will create another service with a method that writes "hello" in the console (SayHelloWorldService). Then we will create a test that instantiates the DummyService bean and runs the dummy method that does nothing, the result will be say hello written on console instead of nothing. This will be achieved by some AOP configuration that will always execute the say hello method before the dummy method is executed.

Versions we will use:
Spring 3.1.1-RELEASE

pom.xml
<repositories>
 <repository>
  <id>springsource-repo</id>
  <name>SpringSource Repository</name>
  <url>http://repo.springsource.org/release</url>
 </repository>
</repositories>

<dependencies>

 <dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.8.2</version>
  <scope>test</scope>
 </dependency>
 
 <!-- These are for Spring beans and to enable the AOP features  -->

 <dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>3.1.1.RELEASE</version>
  <scope>test</scope>
 </dependency>

 <dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.6.12</version>
  <scope>test</scope>
 </dependency>
  
 <dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.6.12</version>
  <scope>test</scope>
 </dependency>

 <dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>2.2.2</version>
  <scope>test</scope>
 </dependency>

</dependencies>

DummyService
package ar.com.pabloExample.services;

public class DummyService {

 public void dummyMethod() {
  
 }
}
This service has a method that does nothing. The final result is that we will always see a Hello World message in the console whenever we run this method. You may be asking how that's possible... well, thanks to AOP whenever we run dummyMethod(), the sayHelloWorld() method will run before...

SayHelloWorldService
package ar.com.pabloExample.services;

public class SayHelloWorldService {
 
 public void sayHelloWorld() {
  System.out.println("--- Hello World!!! ---");
 }
}
This services just prints the Hello World message on the console.

DummyServiceTest
public class DummyServiceTest {
 
 private ApplicationContext applicationContext;
 private DummyService dummyService;

 @Before
 public void init() {
  applicationContext = new ClassPathXmlApplicationContext("test-beans.xml");
  dummyService = (DummyService) applicationContext.getBean("dummyService");
 }

 @Test
 public void dummyMethodTest() {
  
  dummyService.dummyMethod();
 }
}
This is the test that will simply instantiate a DummyService bean and run the dummyMethod() to see the result.


Creating the test-beans.xml with AOP configuration:

For this test to run we need to define these beans:
<bean class="ar.com.pabloExample.services.SayHelloWorldService" id="sayHelloWorldService">
 
<bean class="ar.com.pabloExample.services.DummyService" id="dummyService">

The AOP configuration is:

<aop:config>
 <aop:aspect id="myAspect" ref="sayHelloWorldService">
  <aop:pointcut expression="execution(public void ar.com.pabloExample.services.*.*(..))" id="pointcut1" />
  <aop:before method="sayHelloWorld" pointcut-ref="pointcut1" />
 </aop:aspect>
</aop:config>

<aop:config>
</aop:config>
This tag defines an AOP configuration section in the xml.

<aop:config>
 <aop:aspect id="myAspect" ref="sayHelloWorldService" />
</aop:config>
We declare an aspect referencing the bean with the service we want to execute.

<aop:pointcut expression="execution(public void ar.com.pabloExample.services.*.*(..))" id="pointcut1" />
The pointcut is the condition that must occur so that this aspect runs. In this case the condition is "This aspect will run for any method of any class with any parameter that is under the package ar.com.pabloExample.services and returns a void and is public"

<aop:before method="sayHelloWorld" pointcut-ref="pointcut1" />
This AOP will run the sayHelloWorld() before the dummyMethod() in this case.

So, the xml with the beans ends like this:

test-beans.xml
<beans xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">

 <aop:config>
  <aop:aspect id="myAspect" ref="sayHelloWorldService">
   <aop:pointcut expression="execution(public void ar.com.pabloExample.services.*.*(..))" id="pointcut1" />
   <aop:before method="sayHelloWorld" pointcut-ref="pointcut1" />
  </aop:aspect>
 </aop:config>

 <bean class="ar.com.pabloExample.services.SayHelloWorldService" id="sayHelloWorldService" />
 
 <bean class="ar.com.pabloExample.services.DummyService" id="dummyService" /> 
</beans>

Now you can run the test and see the results.

The same AOP configuration can be done with annotations instead of xml configuration, but I prefer the xml way because AOP configuration is decoupled from the java classes, and a change of AOP configuration doesn't require a new compilation. A really nice example on how to do profiling method execution using annotations can be found here.

The main disadvantage of AOP is that the flow of the program structure may change completely (it depends on how much you depend on aspects...) and it becomes more complex because it is more dificult now to track issues looking at the code.

More information about Spring 3 AOP can be found here

Friday, April 6, 2012

Spring beans injection into EJBs with Snowdrop

Snowdrop is a JBoss project that automatically creates Spring context when the server is being started, scans all deployments for spring beans, and instantiates them as singletons. The advantage of Snowdrop is that the developer can inject Spring Beans in EJBs without taking care of the bean lifecycle. The important thing to mention is that this is a specific JBoss AS oriented product (I mean, it is not like Hibernate that can work on other servers).

It is recommended that the beans that will be deployed in this way are Stateless. Snowdrop will only deploy those beans which are included in the *spring.xml name pattern.

This tutorial will first of all configure JBoss with Snowdrop, and then will show an example with an explanation.

Versions that will be used:
- JBoss 5.1.0.GA
- Snowdrop 2.0.2.Final
- Spring 3.1.1-RELEASE

Part 1 - Configuring JBoss with Snowdrop


1- Download Snowdrop from here
We will choose the Deployer JBoss AS 5 without Spring implementation cause want to use another version from the one it comes with (that version comes with 3.1.0-RELEASE).
2- Unzip into $JBOSS_HOME/server/$PROFILE/deployers
3- Now if you start your server some errors will appear, because there is no spring implementation yet added to the AS
4- The following jars are needed in your $JBOSS_HOME/server/$PROFILE/lib directory (we put them there because we want to make Spring be available to the whole server instance):

spring-context-3.1.1.RELEASE.jar
spring-core-3.1.1.RELEASE.jar
spring-beans-3.1.1.RELEASE.jar
spring-asm-3.1.1.RELEASE.jar
spring-expression-3.1.1.RELEASE.jar
spring-aop-3.1.1.RELEASE.jar
aopalliance-1.0.jar
commons-logging-1.1.1.jar

Read the pom.xml configuration section to see an easy way to get those jars. Now if you start your server no errors should appear which means Snowdrop and Spring are correctly set up in the server!

Part 2 - Developing an example

In this example, we will define a simple Spring bean and a test associated that creates a Spring context and get a bean just to make sure the bean is correctly defined. Then we will create an EJB and inject a Spring bean which was previously instantiated by Snowdrop. Both have the same result (to get a bean) but in different ways.

pom.xml
<repositories>
 <repository>
  <id>springsource-repo</id>
  <name>SpringSource Repository</name>
  <url>http://repo.springsource.org/release</url>
 </repository>
 
 <repository>
  <id>maven-nuxeo</id>
  <name>Maven Nuxeo Repository</name>
  <url>https://maven.nuxeo.org/nexus/content/groups/public/</url>
 </repository>
</repositories>

<dependencies>
 <!-- Since we will use the EJB when it is deployed in the JBoss AS we use this dependency -->
 <dependency>
  <groupId>org.jboss.jbossas</groupId>
  <artifactId>jboss-as-client</artifactId>
  <version>5.1.0.GA</version>
  <type>pom</type>
  <scope>provided</scope>
 </dependency>

 <!-- This allows us to use the Spring annotation for the EJB --> 
 <dependency>
  <groupId>org.jboss.snowdrop</groupId>
  <artifactId>snowdrop-deployers</artifactId>
  <version>2.0.2.Final</version>
  <scope>provided</scope>
 </dependency>

 <!-- This takes all the spring dependencies we put in the server lib folder -->
 <dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>3.1.1.RELEASE</version>
  <scope>provided</scope>
 </dependency>

 <dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.8.2</version>
  <scope>test</scope>
 </dependency>
</dependencies>

I had to add the Maven Nuxeo Repo cause there where some dependencies being retrieved from invalid places (http://repository.jboss.org/maven2/ which is unavailable)

The following xml has the definition of the bean, with the data it will have. This one has to be under the test/resources classpath folder.

test-beans.xml
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

 <bean id="simplePerson" class="ar.com.pabloExample.model.Person">
  <property name="id" value="3" />
  <property name="name" value="Ruben" />
 </bean>
</beans>

The Person class is a POJO with id and name attributes with getters and setters, nothing special.

The next one is similar to the previous xml but it has the description tag which is  used in the injection. This one has to be under the java/resources classpath folder because this one will be deployed inside the jar.

beans-spring.xml
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

 <description>BeanFactory=(MyApp)</description>

 <bean id="simplePerson" class="ar.com.pabloExample.model.Person">
  <property name="id" value="3" />
  <property name="name" value="Ruben" />
 </bean>
</beans>


This test creates a Spring context with the test-beans.xml and then asks that context for the bean.

PersonBeanTest.java
public class PersonBeanTest {
 
 private ApplicationContext applicationContext;

 @Before
 public void init() {
  applicationContext = new ClassPathXmlApplicationContext("test-beans.xml");
 }

 @Test
 public void simplePersonInstantiatonTest() {
  Person simplePerson = (Person) applicationContext.getBean("simplePerson");
  
  System.out.println("SimplePerson name: " + simplePerson.getName());
  System.out.println("SimplePerson id: " + simplePerson.getId());
 }
}

Now, to show the real power of Snowdrop, here is the EJB:

PersonServiceBean.java
@Stateless
@Interceptors(SpringLifecycleInterceptor.class)
public class PersonServiceBean implements PersonService, PersonServiceRemote {
 
 @Spring(bean = "simplePerson", jndiName = "MyApp")
 private Person simplePerson;
 
 @Override
 public void writeSimplePersonData() {

  System.out.println("Will write the name of the person that has been injected with snowdrop:");
  System.out.println("Name: " + simplePerson.getName());
  System.out.println("ID: " + simplePerson.getId());
 }
}

It can be seen that with the @Spring annotation we get the simplePerson bean defined in the beans-spring.xml file.

PersonServiceTest.java
public class PersonServiceTest {
 
 private PersonService service;

 @Before
 public void init() throws NamingException {
  InitialContext context = new InitialContext();
  service = (PersonService) context.lookup("PersonServiceBean/remote");
 }
 
 @Test
 public void simplePersonWrite() {
  service.writeSimplePersonData();
 }
}


In order to run this test you have to deploy the jar file which can be generated from this project with:
mvn clean package -Dmaven.test.skip=true
take the generated jar and deploy it in $JBOSS_HOME/server/$PROFILE/deploy and run your server so that the lookup of the EJB is successful.

Additional documentation about Snowdrop can be found in their official site