Archive

Posts Tagged ‘spring’

Spring + Quartz + Clustering

December 16th, 2009

En Javi’s Java podemos encontrar un estupendo post a cerca de Quartz, sobre qué es y cómo se configura dentro de nuestra aplicación Web por lo que no es necesario hablar mucho más del tema. En este post sólo me gustaría resaltar la sencillez y elegancia resultante de la integración de Quartz con Spring, como el mismo Javi comentaba en uno de los comentarios, que nos permite configurar una tarea en apenas un par de minutos.

Tampoco es necesario escribir ninguna línea de código como ejemplo para la integración con Spring ya que la documentación de Spring sobre el tema (cronExpression) es clara y sencilla.

¿Quién necesita ahora los crones de Linux o las tareas de Windows para programar tareas relacionas con nuestra aplicación Web?

Si además necesitamos ejecutar la aplicación en distintas instancias y no queremos que haya problemas, como que el proceso se ejecute simultáneamente en todas ellas, podemos configurar Quartz para que se ejecute en Clustering añadiendo sólo unas cuántas líneas extras en la configuración. Ejemplo de configuración en Spring para la ejecución de Quartz en clustering:

<bean name="job"
   class="org.springframework.scheduling.quartz.JobDetailBean">
    <property name="jobClass" value="whatabout.Job" />
    <property name="jobDataAsMap">
        <map>
            <!-- properties
            <entry key="timeout" value="" />
            -->
        </map>
    </property>
</bean>

<bean id="cronTrigger"
   class="org.springframework.scheduling.quartz.CronTriggerBean">
    <property name="jobDetail" ref="job" />
    <property name="cronExpression" value="0 0 6 * * ?" />
</bean>

<bean
    class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="cronTrigger" />
        </list>
    </property>
    <property name="applicationContextSchedulerContextKey">
        <value>applicationContext</value>
    </property>
    <property name="startupDelay" value="0" />
    <property name="waitForJobsToCompleteOnShutdown" value="true" />
    <property name="dataSource" ref="myDataSource" />
    <property name="quartzProperties">
        <props>
            <!-- ThreadPool -->
            <prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
            <prop key="org.quartz.threadPool.threadCount">5</prop>
            <prop key="org.quartz.threadPool.threadPriority">5</prop>
            <!-- Job store -->
            <prop key="org.quartz.jobStore.misfireThreshold">60000</prop>
            <prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
            <prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.StdJDBCDelegate</prop>
            <prop key="org.quartz.jobStore.useProperties">false</prop>
            <!-- Clustering -->
            <prop key="org.quartz.jobStore.isClustered">true</prop>
            <prop key="org.quartz.scheduler.instanceId">AUTO</prop>
            <prop key="org.quartz.jobStore.clusterCheckinInterval">30000</prop>
        </props>
    </property>
</bean>

Evidéntemente es necesario configurar un Datasource (myDataSource en el ejemplo) y crear las tablas de Quartz oportunas. He de añadir que en uno de los últimos proyectos en los que he trabajado hemos montado Quartz en Clustering con cinco máquinas con éxito y después de varios meses en producción ningún proceso se ha ejecutado más de una vez o ninguna.

Referencia:

About job , , , ,

Spring - JPA - Hibernate

December 5th, 2008

Estos días he tenido que montar el esqueleto para un nuevo proyecto. Para mi sorpresa la combinación de Frameworks que se requerían no estaba entre las disponibles de Appfuse Light así que tuve que montarla casi desde cero.

Son muchos los ejemplos y la documentación que se puede encontrar que hablan acerca de cómo montar JPA con Spring, pero como suele suceder casi todos proceden de un ejemplo original por lo que al final todos quedan reducidos a muy pocos que para colmo, están incompletos. Así pues, voy a hacer mi pequeña aportación y describiré el archivo de Spring al completo. No describiré cómo se realizan Test o cómo se crean claves primarias compuestas porque son temas que ya tratan bien en otros blogs.

En primer lugar, empezaré definiendo una entidad, sobre la que girará el ejemplo: User.

package es.whatabout.model;

@Entity
@Table(name = "USER")
public class User{

    @Id
    @Column(name = "ID")
    private Integer idUser;

    @Column(name = "FIRST_NAME")
    private String firstName;

    @Column(name = "LAST_NAME")
    private String lastName;

    ...get/set methods...

}

Como se puede ver, esta clase lleva anotaciones JPA que identifican a esta clase como un entidad, su clave primaria y el mapeo de sus atributos a la tabla y columnas correspondientes en la base de datos. Esta será la única clase que contenga anotaciones ya que el resto de la configuración, como la transaccionalidad, la configuraremos desde Spring.

Antes de definir la clase DAO crearemos un interfaz genérico y su implementación que proporcionará a nuestro dao particular y a todos los que generemos (y su implementaciones) de todos los métodos CRUD gracias a los tipos genéricos de Java.

package es.whatabout.dao;

import java.io.Serializable;
import java.util.List;

public abstract interface GenericDao <T, PK extends Serializable> {

    public abstract List<T> getAll();

    public abstract T findByPK(PK id);

    public abstract void update(T object);

    public abstract void remove(T object);

    public abstract void insert(T object);

}

Y su implementación.

package es.whatabout.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.orm.jpa.support.JpaDaoSupport;

import es.whatabout.dao.GenericDao;

public class GenericDaoImpl extends JpaDaoSupport
        implements GenericDao {

    private static Log logger =
            LogFactory.getFactory().getInstance(GenericDaoImpl.class);

    private Class persistentClass;

    public GenericDaoImpl() {
        this.persistentClass =
                (Class) ((ParameterizedType) getClass()
                    .getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public void insert(T t) {
        getJpaTemplate().persist(t);
    }

    public T findByPK(PK id) {
        return getJpaTemplate().find(persistentClass, id);
    }

    public List getAll() {
        List entities = new ArrayList();

        try {
            String s =
                    "select c from " + persistentClass.getSimpleName() + " c";
            entities = getJpaTemplate().find(s);
        } catch (Exception e) {

            logger.error(e.getStackTrace());
        }
        return entities;
    }

    public void remove(T t) {
        getJpaTemplate().remove(getJpaTemplate().merge(t));
    }

    public void update(T object) {
        getJpaTemplate().merge(object);
    }

}

T será la entidad que maneje nuestra clase dao mientras que PK será la clase de la clave primaria de ésta, que puede ser de tipo simple o compuesta (composed primary key)

A continuación crearemos una interfaz para un dao particular y su implementación utilizando el interfaz y la implementación de éste antes definido.

package es.whatabout.dao;

import es.whatabout.model.User;

public interface UserDao extends GenericDao<User, Integer> {

}

Vemos que nuestro interfáz es muy simple, ya que dispone de los métodos CRUD del interfaz GenericDao. Del mismo modo, su implementación también será muy simple ya que ésta extiende de la implementación de GenericDao, GenericDaoImpl

package es.whatabout.dao.impl;

import es.whatabout.dao.UserDao;
import es.whatabout.model.User;

public class UserDaoImpl extends GenericDaoImpl<User, Integer>
        implements UserDao{
}

Sólo queda definir el archivo de configuración de Spring:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
		http://www.springframework.org/schema/tx
		http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
		http://www.springframework.org/schema/aop http://
		www.springframework.org/schema/aop/spring-aop-2.5.xsd">

	<!-- archivo de configuracion para los datos de conexion -->
	<bean id="propertyConfigurer"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="location" value="classpath:jdbc.properties" />
	</bean>

	<!-- Transaccionalidad para todas las clases del paquete indicado -->
	<aop:config>
		<aop:advisor id="managerTx" advice-ref="txAdvice"
			pointcut="execution(* es.whatabout.dao.impl.*.*(..))" />
	</aop:config>

	<!-- restringimos a solo lectura las transacciones metodos que comienzan por get -->
	<tx:advice id="txAdvice">
		<tx:attributes>
			<tx:method name="get*" read-only="true" />
			<tx:method name="*" />
		</tx:attributes>
	</tx:advice>

	<!-- configuramos el transaction  manager-->
	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="dataSource" ref="dataSource" />
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>

	<!-- Conexion a bd con las propiedades del arhivo jdbc.properties-->
	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>

	<!-- configuramos la Factory Manager, que trabajará con hibernate-->
	<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="jpaVendorAdapter">
			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
				<property name="showSql" value="true" />
				<property name="databasePlatform" value="${hibernate.dialect}" />
			</bean>
		</property>
		<property name="jpaProperties">
			<value>hibernate.show_sql=true</value>
		</property>
	</bean>

	<!-- configuramos LOG4J -->
	<bean id="log4jInitialization"
		class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
		<property name="targetClass" value="org.springframework.util.Log4jConfigurer" />
		<property name="targetMethod" value="initLogging" />
		<property name="arguments">
			<list>
				<value>classpath:log4j.xml</value>
			</list>
		</property>
	</bean>

	<!-- inyeccion del entityManagerFActory a nuestro Dao particular-->
	<bean id="userDao" class="es.whatabout.dao.impl.UserDaoImpl">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>

</beans>

Con este archivo de cofiguración ya podemos disponer de nuestro dao capaz de realizar todas las operacoines CRUD sin ningún proplema.

Referencia: The Spring Framework - Reference Documentation, chapter 9, Introduction to Spring 2 and JPA
Código: descargar

About job , , , ,