Archive

Archive for December, 2009

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 , , , ,

Mi modelo de datos

December 11th, 2009

Hoy me ha venido el jefe diciendo que hay que hacer una aplicación para gestionar los usuarios de la empresa. Según he entendido, creo que vamos a necesitar tres tablas: EMPLEADO, DIRECCION y CIUDAD de empleado.

¿Cómo plantemos las relaciones entre las tablas? Creo que un principio podría centrarme en la tabla EMPLEADO y que ésta tuviera una clave foránea a la tabla DIRECCION, pero no sé, no me convence. ¿Y si un empleado no tiene dirección? Le pregunto al analista funcional (aquel que ha echado un ojo a los dos folios del documento de requisitos) y me dice que nunca se va a dar el caso en el que tengamos un empleado sin dirección. De todas formas, para curarme en salud, creo que mejor centrarme en la tabla DIRECCION y que ésta tenga dos claves foráneas: id_empleado e id_ciudad. Perfecto, así me curo en salud y ahora un usuario puede no tener ninguna dirección o tener N. Sí, el jefe dice que esto no pasará en la vida, pero yo prefiero pensar en posibles evolutivos que para eso soy más listo y con visión de futuro.

Vale, ahora que ya he decidido cómo van a ser las relaciones entre las tablas veamos ahora los campos de éstas.

Tabla EMPLEADO:

  • ID_EMPLEADO. Mejor concatenar el nombre de la tabla con el nombre del campo para estar seguro siempre de qué ID hablamos (siempre pensando en los demás y en el futuro). De tipo VARCHAR(100) además me voy a currar un mega algoritmo que cree genere los ID en función de la hora, la cotización del IBEX 35 y la hora a la que llega mi compi al curro, así me aseguraré de que jamás en la vida se repita un identificador.
  • FECNA_TRABAJADOR. VARCHAR(10) . He decidido no poner el literal “fecha nacimiento” al completo porque una vez oí que cobran por caracteres.
  • DIA, MES y ANIO. Estos tres campos lo utilizaremos para guardar la fecha de incorporación del empleado. Todos VARCHAR(4). Sé que sólo necesitaría VARCHAR(2) para el día y el mes, pero mejor homogeneizar un poco.
  • NOM_DIRECCION. VARCHAR(100). Nombre del empleado.

Ok, decido no describir los campos (los nombres son descriptivos por sí solos) ni indicar cuales de ellos pueden ir a NULL.

Tabla DIRECCION:

  • ID_DIR. NUMBER(10) que luego mapearé con un Float o Double.
  • CALLE. VARCHAR(200).
  • NUMERO. VARCHAR(5).
  • TLFNO_DIREC. VARCHAR(10).
  • CIUDAD. VARCHAR(32). He decidido guardar también en esta tabla el literal del la ciudad para que no haya que cruzar con la tabla CIUDAD las consultas. Esto agradecerá el rendimiento.
  • ID_CIUDAD. Identificador de la ciudad.
  • ID_EMPL. Identificador de empleado.
  • COM. VARCHAR(50). Creo que nunca viene mal crear un campo comodín por si el día de mañana hace falta algún campo extra. Así no habrá que modificar el modelo.

Tabla CIUDAD:

  • ID. VARCHAR(100);
  • VALUE. NUMBER(3). ¿cuál es el equivalente a NUMBRE en Java? Ya sé, Number.
  • DESCRIPTION. VARCHAR(100)
  • FC_CREACION, FC_MODIFICACION. Ambos de tipo DATE. Con estos campos llevaré un control sobre cuando se crean y actualizan los datos.

Qué bien me está quedando el modelo.

De repente me viene otra vez el jefe y me dice que hace falta crear otra tabla para guardar las direcciones de las oficinas y que no puede ser la que ya tenemos. Me pongo al lío:

Tabla DIRECCION… no, el nombre ya está pillado… DIRECCION_OFICINAS… muy largo y vaya ser que cobren por caracteres. Ya sé, ADDRESS. Que se note que sé idiomas.

Ya está todo listo. ¿Qué más puedo hacer para mejorar el modelo? Ya sé, voy a desactivar las claves foráneas. He oído que fastidian el rendimiento y quiero que la aplicación vaya super-rápida además, las claves foráneas es algo que está sobrevalorado, en realidad no son tan necesarias. Debemos confiar más en que la gente mantendrá los datos de forma coherente.

Ahora me voy a poner con la capa DAO. He oído que hay por ahí un algo que se llama “Hibernate” que hace las cosas pero a mí eso no me convence. Me gusta saber lo que hago y llevar el control en todo momento y esto no se puede hacer con los frameworks que no te dejan liberta al desarrollador.

¡Cagüen! Me voy que ya ha pasado un minuto de la hora de irme. Otro día sigo con la capa DAO.

About job ,