Tareas programadas con Spring y Quartz

En un post anterior explicamos cómo utilizar las clases wrapper de Spring para ejecutar una tarea a intervalos regulares de tiempo dentro de una aplicación web J2EE. En esta publicación integraremos Spring con el planificador Quartz para programar la misma tarea dentro de una aplicación J2EE. Quartz es un servicio opensource de planificación de tareas que puede ser integrado con cualquier aplicación Java, desde la más pequeña aplicación stand-alone al más extenso sistema de e-commerce

En un post anterior explicamos cómo utilizar las clases wrapper de Spring para ejecutar una tarea a intervalos regulares de tiempo dentro de una aplicación web J2EE. En esta publicación integraremos Spring con el planificador Quartz para programar la misma tarea dentro de una aplicación J2EE.

Quartz es un servicio opensource de planificación de tareas que puede ser integrado con cualquier aplicación Java, desde la más pequeña aplicación stand-alone al más extenso sistema de e-commerce. Quartz puede ser utilizado para crear planificaciones simples o complejas para ejecuciones de decenas, cientos, o incluso miles de trabajos cuyas tareas son definidas como componentes Java estándar. Además incluye soporte para transacciones JTA y clustering. Es de uso libre bajo la licencia Apache 2.0.

Para instalarla en nuestra aplicación simplemente descargaremos el fichero quartz-x.x.x.tar.gz desde la web de descargas y extraeremos el fichero quartz-all-x.x.x.jar para incorporarlo a nuestra aplicación. En mi caso utilicé la versión 1.8.6, pese a no ser la última, debido a mi versión de Spring. Si tienes problemas con la última, te recomiendo que vayas bajando de versión hasta dar con la idónea. Dentro del fichero comprimido tar.gz existe una carpeta lib con las dependencias que tiene quartz con otras librerías. En mi caso necesité incluir en mi aplicación la librería slf4j-api-x.x.x.jar.

Lo primero que vamos a crear es la tarea a ejecutar. Será un método público dentro de una clase pública, tal y como se muestra en el siguiente código:

package es.activity.schedule;

public class RunMeTask
{
	public void printMe() {
		System.out.println("Ejecutando tarea programada");
	}
}

A continuación abrimos para editar el fichero XML de contexto de nuestra aplicación web Spring y añadimos un bean que haga referencia a la clase anterior:

<bean id="runMeTask" class="es.activity.schedule.RunMeTask" />

Una vez definida la tarea pasamos a configurar los principales conceptos de Quartz: Job, Trigger y Scheduler:

Job

Es la unidad de trabajo o tarea a ejecutar. Sería cualquier implementación de la interfaz org.quartz.Job. En Spring se han incluido varias implementaciones de la interfaz pero nosotros vamos a utilizar MethodInvokingJobDetailFactoryBean, la cual nos permite la ejecución de un método de cualquier clase java:

<bean id="schedulerJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
   <property name="targetObject" ref="runMeTask"></property>
   <property name="targetMethod" value="printMe"></property>
   <property name="concurrent" value="false"></property>
</bean>

Trigger

Es la unidad que conoce los detalles de la ejecución de la tarea y los instantes en los que se debe ejecutar. Sería cualquier implementación de la interfaz org.quartz.Trigger. Es este caso es el propio Quartz el que proporciona diversas implementaciones:

SimpleTrigger

Permite especificar una hora de comienzo, una hora de fin y un intervalo de ejecución, similar al ejemplo del post en el que hablábamos sobre la clase Timer incluida en el JDK. En el ejemplo ejecutamos el job tras un segundo de retardo y posteriormente cada 60 segundos.

<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
        <property name="jobDetail" ref="schedulerJob" />
        <property name="repeatInterval" value="60000" />
        <property name="startDelay" value="1000" />
</bean>

CronTrigger

Permite especificar expresiones cron de Unix para establecer fechas y horas de ejecución del job. En el ejemplo se ejecuta el job de lunes a viernes a las 8 de la mañana.

<bean id="timerCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">>
        <property name="jobDetail" ref="schedulerJob"></property>
        <property name="cronExpression" value="0 0 8 ? * MON-FRI"></property>
</bean>

Más información sobre expresiones cron en los siguientes enlaces:

  1. http://en.wikipedia.org/wiki/CRON_expression
  2. http://www.quartz-scheduler.org/documentation/quartz-1.x/examples/Example3

Scheduler

Por último tenemos que lanzar la ejecución de nuestro job mediante un planificador que gestione la ejecución a partir de la información especificada en el Trigger. Spring incorpora la clase SchedulerFactoryBean para ejecutar uno o varios trigger:

<bean id="GiaScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="timerCronTrigger"></ref>
            </list>
        </property>
</bean>

Con todo esto especificado en el fichero de contexto xml, desplegamos la aplicación en el servidor de aplicaciones y se ejecutará la tarea con la programación especificada.

 

Tareas programadas con Spring Framework y JDK Timer

Seguro que en más de una ocasión, durante el desarrollo de una aplicación has necesitado ejecutar una tarea de forma periódica. Para desarrollar esto, desde la versión 1.3 del JDK se incluye las clases java.util.Timer y java.util.TimerTask. Estas clase facilitan la programación de tareas para ejecuciones futuras en un thread en segundo plano.

Seguro que en más de una ocasión, durante el desarrollo de una aplicación has necesitado ejecutar una tarea de forma periódica. Para desarrollar esto, desde la versión 1.3 del JDK se incluye las clases java.util.Timer y java.util.TimerTask. Estas clase facilitan la programación de tareas para ejecuciones futuras en un thread en segundo plano. Las tareas pueden ser programadas para ejecutarse una vez o múltiples veces a intervalos regulares.

En el caso de que necesites una planificación más compleja, la clase Timer se quedará corta en varios aspectos y deberás utilizar un planificador como Quartz o similar. En este hilo de StackOverflow puedes ver otras alternativas. En este artículo vamos a utilizar las clases wrapper de Spring, que internamente hacen uso de las del JDK, para ejecutar una tarea a intervalos regulares de tiempo dentro de una aplicación web J2EE con el framework de Spring.

Lo primero que vamos a crear es la tarea a ejecutar. Será un método público dentro de una clase pública, tal y como se muestra en el siguiente código:

package es.activity.schedule;

public class RunMeTask
{
	public void printMe() {
		System.out.println("Ejecutando tarea programada");
	}
}

A continuación abrimos para editar el fichero XML de contexto de nuestra aplicación web Spring y añadimos un bean que haga referencia a la clase anterior:

<bean id="runMeTask" class="es.activity.schedule.RunMeTask" />

Vamos a definir a continuación, en el mismo fichero, el método de la clase que ejecutará el código de la tarea. Spring incorpora la clase MethodInvokingTimerTaskFactoryBean para reemplazar la clase TimerTask del JDK.

<bean id="schedulerTask" 
  class="org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean">
	<property name="targetObject" ref="runMeTask" />
	<property name="targetMethod" value="printMe" />
</bean>

Ahora vamos a definir cada cuánto se ejecutará utilizando la clase ScheduledTimerTask, que reemplaza a la clase Timer del JDK. Le pasamos como propiedades al bean tanto el retardo (delay) para la primera ejecución, como el intervalo (period) para sucesivas ejecuciones. Ambos valores en milisegundos. En el ejemplo se ejecutará un segundo después del despliegue de la aplicación y a continuación cada 60 segundos.

<bean id="timerTask"
	class="org.springframework.scheduling.timer.ScheduledTimerTask">
	<property name="timerTask" ref="schedulerTask" />
	<property name="delay" value="1000" />
	<property name="period" value="60000" />
</bean>

Hasta ahora se trata de configurar de manera declarativa lo referente a la tarea. Vamos a ejecutar el timer para que comience la ejecución:

<bean class="org.springframework.scheduling.timer.TimerFactoryBean">
	<property name="scheduledTimerTasks">
		<list>
			<ref local="timerTask" />
		</list>
	</property>
</bean>

Cómo ves no es necesario introducir ninguna llamada por código a la tarea programada. La clase TimerFactoryBean se encarga de ejecutar el código del método PrintMe() durante el despliegue de la aplicación en el servidor, con un retardo de 1 segundo la primera vez y luego cada 60 segundos.

Actualización (24/01/2013). Para completar la información quizás te interese cómo integrar Spring con el planificador Quartz. Por ello publiqué otro artículo posteriormente con el título “Tareas programadas con Spring y Quartz”.

Modificar Brillo y Contraste con Java 2D

Como podéis comprobar, en el blog comento sobre ciertos temas o problemas que van surgiendo durante nuestro trabajo diario y que damos solución mediante Java y Javascript (o cualquier otra tecnología, mayoritariamente orientada a la web). Hace poco publiqué un post sobre cómo realizar zoom en imágenes publicadas en la aplicación web. Este problema afortunadamente pudimos resolverlo mediante javascript y jQuery. Tras esto, el cliente nos propuso como requisito realizar ajustes de brillo y contraste sobre imágenes digitalizadas y publicadas en nuestra aplicación.  Lo primero que se nos ocurrió fue incorporar alguna librería javascript para abordar lo que nos proponían, pero no encontramos nada adecuado que funcionara en todos los navegadores (esperaremos a HTML5 y el objeto canvas). La otra opción era, obviamente, realizar una petición al servidor para que nos enviara la imagen transformada con los datos de brillo y contraste actualizados.

El lenguaje Java incorpora la librería Java2D, cuyo API proporciona un amplio conjunto de clases para la composición y transformación de la imagen. La solución consistía en realizar una petición al servidor para recargar la página y actualizar la imagen, de manera que en el atributo src de la imagen realizara una petición al servidor para obtener una nueva imagen transformada, pasando los nuevos parámetros para brillo y contraste:

<img width="687" height="960" style="position: absolute; top: 0px; left: 82px;" src="/MiApp/ImageServlet?accion=42&id_imagen=1&brillo=10.0&contrast=1.0" />

En la página incorporamos cinco botones: más brillo, más contraste, menos brillo, menos contraste y restablecer brillo y contraste originales. Establecemos una escala de manera que los valores que podamos asignar al brillo van desde -125 hasta 125 (por defecto 0) y para el contraste desde 0.0 hasta 2.0 (por defecto 1.0). Cada vez que pulsamos los botones de brillo actualizamos la página para que el valor del parámetro brillo, dentro del atributo src de la imagen, se incremente o decremente en 5 unidades (o lo que deseemos). Haremos lo mismo en el caso del contraste con una variación de +0.1 ó -0.1 unidad (también modificable). El botón restablecer deja los parámetros en 0.0 para el brillo y 1.0 para el contraste. En resumen, los botones recargan de nuevo la página, pasando los nuevos valores actualizados de brillo y contraste, aumentando o decrementando su valor en función del botón pulsado.

El controlador que sirve la imagen en el servidor (en el ejemplo de arriba sería el servlet ImageServlet cuando recupera el parámetro accion con el valor 42) recogerá dichos parámetros, y cargará la imagen en memoria desde el archivo de un modo similar a éste:

JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(new BufferedInputStream(new FileInputStream(ruta)));
BufferedImage buf = decoder.decodeAsBufferedImage();

El parámetro ruta es la ruta a la imagen en el sistema de ficheros (he omitido cómo recuperar la ruta a partir del identificador de imagen pasado como parámetro). Una vez cargada la imagen invocaremos al método que realizará la transformación, pasando como parámetro la referencia a la imagen y los valores de brillo y contraste:

buf = brightContrastChange(buf, brillo, contraste);

Y el código fuente de la función es la siguiente:

private static BufferedImage brightContrastChange(BufferedImage bufferedImagen, Float brillo, Float contraste)
{
//Transformación de la imagen con nuevos valores brillo y contraste
BufferedImageOp operacion = new RescaleOp(contraste, brillo, null);
bufferedImagen = operacion.filter(bufferedImagen, null);
return bufferedImagen;
}

Únicamente nos queda escribir la imagen en el outputStream para enviarla al navegador:

JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(output);
encoder.encode(bufferedImage);
output.flush();

Siendo output el outputStream o flujo de salida. Obviamente estos son los pasos básicos, pero se pueden incorporar numerosas mejoras.

Una de ellas es la de obtener la imagen mediante una petición AJAX, sin necesidad de recargar la página para actualizar los parámetros brillo y contraste. Otra es la de mostrar una barra sobre la que podamos seleccionar un valor determinado sin saltos de 5 en 5 (brillo) o de 0.1 en 0.1 (contraste). Un componente que nos ayudó enormemente a acometer esta funcionalidad fue el slider de jquery.ui y que comentaré en otra entrada del blog próximamente.

Tablas con DisplayTag y Ajax

Durante los dos últimos años, mi equipo y yo hemos introducido en los proyectos J2EE una librería de tags JSP para la publicación de datos de forma tabular. Se trata de DisplayTag, una librería open source muy potente que nos facilita la visualización de tablas de datos y la ordenación y paginación de los mismos empleando muy poco código. Para incluirla basta con añadir en la página JSP el tag <display:table>, indicarle el nombre de las columnas, así como el nombre de la colección de datos que enviamos desde el servidor en la respuesta. La librería se encarga de generar el código html para presentar los datos en la tabla, generar los controles de paginación y enlaces de ordenación de las columnas.

Una de las funcionalidades que no incluye esta librería es la utilización de AJAX,  que nos acerca una serie de técnicas de programación para la creación de aplicaciones interactivas. Cada vez que pulsamos para ordenar la tabla o para pasar a la siguiente página de resultados se realiza una petición al servidor y éste devuelve una nueva página recargando la anterior con la nueva información. Sería conveniente que por cada petición de ordenar o cambiar la página de resultados se recargase únicamente la información de la tabla de forma asíncrona y no toda la página.

Solución con Ajax Tags

La librería Ajax Tags es un conjunto de tags JSP que simplifica el uso de la tecnología AJAX en las páginas JSP. Además incorpora un tag específico para permitir y habilitar AJAX en las Displaytags comentadas más arriba. Para utilizarlo basta con envolver la tabla con el tag <ajax:displayTag> de manera que sólo se refresque el área contenida en éste y no toda la página:

<ajax:displayTag id="displayTagFrame" ajaxFlag="displayAjax">

    <display:table name="service.allCars" pagesize="10" scope="page"
        defaultsort="1" defaultorder="descending" export="true" id="row"
        excludedParams="ajax">
        <display:column property="make" title="Make" sortable="true"
            headerClass="sortable" />
        <display:column property="model" title="Model" sortable="true"
            headerClass="sortable" />
        <display:column title="Link" media="html">
            <a href="http://www.${row.make}.com">${row.make} Web Page</a>
        </display:column>
        <display:column title="Link" media="excel xml"> www.${row.make}.com </display:column>
    </display:table>

</ajax:displayTag>

¿Cómo funciona? Engloba todo el código html de la tabla en un div con el id indicado como parámetro, sobreescribe el método onclick de cada enlace de la tabla por un código que se encarga de realizar la llamada al servidor y recargar únicamente el div indicado con el código de la tabla (obviando todo el código que no sea la tabla).

Aun resultando así de fácil nos hemos encontrado dos problemas:

  1. Nosotros utilizamos Struts/Tiles para componer la maquetación de las páginas (aunque nuestro framework sea Spring), de manera que dividimos la página en varias JSPs (cabecera, menú, pie y contenido). Cómo hemos dicho anteriormente ajaxtags obvia el código que no sea de la tabla pero únicamente lo hacía para el contenido, por lo que en el div volvía a aparecer la cabecera, el pie, etc. La solución fue crear una definición en la configuración de Tiles que sólo incluyese el contenido y si en la petición venía cierto parámetro, redirigir desde el servidor a ésta.
  2. Ajaxtags sobreescribe el evento onclick de los enlaces, pero también sobreescribe el atributo href con un javascript://nop/. Esto nos supone un problema si el sitio tiene alguna restricción de accesibilidad, puesto que en navegadores que no tengan javascript activo (o no soporten js) no funcionará la paginación y ordenación de la tabla.

Solución con jQuery

jQuery es una librería Javascript que simplifica el desplazamiento entre documentos, manipulación del DOM, la gestión de eventos, los efectos y animaciones y la interacción mediante Ajax para un rápido desarrollo web. Esta solución se basa en la funcionalidad load de jQuery, la cual permite cargar html externo desde una fuente remota e inyectarlo en el DOM.

Vamos a verlo más fácil con el siguiente ejemplo:

<div id="tablaresultados">

    <display:table name="modelo.resultado"
        requestURI="/catalog/busquedaTexto.do" pagesize="10" cellspacing="0"
        decorator="es.displaytag.decorator.BuscaTextoTableDecorator"
        id="resultados">
        <display:column title="Título" property="titulo" sortable="true" />
        <display:column title="Signatura" property="signatura" sortable="true" />
        <display:column title="Fechas" property="fecha" style="text-align:center" sortable="true" />
    </display:table>

</div>

El único requisito en la tabla será que la contenga una capa con el id que deseemos. En esta solución sí que es necesario introducir código javascript en la página:

<script>

    if (!com) var com = {};
    es.displaytag= {
            onPeopleTableLoad: function() {

                // Se llama cuando se cargan los datos
                $("table#resultados th.sortable").each(function() {
                    // Itera sobre cada cabecera de columna conteniendo la clase sortable, de manera
                    // que
                    // podemos sobreescribir la gestión de click via ajax, en lugar de
                    // permitir al navegador seguir un enlace normal
                    $(this).click(function() {
                        // "this" es el elemento th ordenable
                        var link = $(this).find("a").attr("href") + " #tablaresultados";
                        $("div#tablaresultados").load(link, {}, es.displaytag.onPeopleTableLoad);
                        // Paramos la propagación de eventos, sin permitir que el navegador ejecute el
                        // href
                        return false;
                    });
                });
                $("div#tablaresultados .pagelinks a").each(function() {
                    // Itera sobre cada enlace de paginación para sobreescribirlo
                    $(this).click(function() {
                        var link = $(this).attr("href") + " #tablaresultados";
                        $("div#tablaresultados").load(link, {}, es.displaytag.onPeopleTableLoad);
                        return false;
                    });
                });
            }
    };

    $(document).ready(function() {
        // Carga inicial cuando el DOM está preparado. Se asume que vas a inyectar
        // dentro de un div
        // con el id "tablaresultados" que existe en la página.
        $("div#tablaresultados").load("/path/remoto/que/genera/tabla/arriba", {}, es.displaytag.onPeopleTableLoad);
    });

</script>

Para evitar el problema 1 comentado en la solución con ajaxtags, añadimos a los enlaces el id “#tablaresultados” para indicarle que recargue únicamente la capa que contiene la tabla. El problema 2 no lo tenemos con esta solución puesto que no se sobreescribe el atributo href. En el caso de mostrar la tabla en un navegador que no interprete javascript la tabla funcionará normalmente recargando toda la página.

Conclusiones

Personalmente me inclino por la solución con jQuery, ya que en nuestro caso no es necesario modificar código en el servidor y porque necesitamos cumplir con criterios de accesibilidad.

Seguramente existen multitud de componentes o tablas con Ajax para incorporar a las aplicaciones web, pero si ya tienes una página con displaytag, el código en el servidor que la alimenta y deseas incorporar ajax y mantener lo que tenías, estas dos soluciones son unas muy buenas opciones.

Actualización

(25-03-2011) Posterior a esta entrada se desarrolló un plugin jQuery (displaytag-ajax) que provee la capacidad de refresco AJAX a tablas generadas mediante Display Tag. Debo dar las gracias a Yajairo87 ya que gracias a su comentario pude descargármelo y probarlo (funciona perfectamente). Si tenéis curiosidad y miráis el código comprobaréis que realiza el mismo proceso comentado en esta entrada. Podéis descargarlo en la siguiente dirección: http://joseantoniosaiz.es/samples/displaytag-ajax/displaytag-ajax-1.2_0.zip

Gestión de Ramas con Subversion y Eclipse IDE

Crear ramas, etiquetar y realizar merge son conceptos comunes para casi todos los sistemas de control de versiones. En este artículo vamos a adentrarnos en la gestión de ramas dentro de un proyecto de desarrollo con Eclipse. Este proyecto lo tenemos enlazado a Subversion utilizando el plugin Subclipse. Gestionar ramas (branching) es una parte fundamental de un control de versiones, se asume que estás familiarizado con los conceptos básicos de Subversion.

¿Qué es una rama?

Se trata de una línea de desarrollo que existe independientemente de otra. Una rama siempre es una copia de otra y desde el momento de copia contiene su propia historia. Subversion permite mantener ramas paralelas de tus ficheros y directorios. Ayuda a duplicar cambios de una rama a otra y, finalmente, puede hacer que se reflejen cambios de tu copia de trabajo en diferentes ramas por lo que puedes mezclar y enlazar diferentes líneas de desarrollo en tu trabajo diario.

Sincronizar ramas Subversion

Crear una rama

Vamos con un caso práctico: Lo normal es que el proyecto esté asociado a la línea principal de desarrollo, que llamamos Trunk. Todas las modificaciones y funcionalidades que se deseen incorporar a la versión de producción deberán estar contenidas en esta rama (pueden existir otras estrategias pero ésta es la que usamos en nuestro trabajo). En un momento determinado nos puede interesar abrir otra línea de desarrollo o rama en la que trabajará de forma independiente en el tiempo un desarrollador o equipo de desarrolladores, por ejemplo. Así dejamos la copia de trabajo del Trunk libre por si es necesario una nueva versión de producción de manera inmediata para corregir una incidencia o incorporar una funcionalidad urgente. Para realizar esto hacemos clic sobre el proyecto con el botón derecho del ratón y vamos a Team -> Branch/Tag. Subversion considera exactamente igual una rama (branch) que una etiqueta (tag), es decir, son copias de un conjunto de carpetas y ficheros (se diferencian en que las ramas tienen history y las etiquetas no).

A continuación seleccionamos el directorio en el que se almacenará esta rama. Todas las ramas se suelen guardar bajo el directorio branches del repositorio por convenio. También debemos indicar la revisión sobre la que queremos crear la rama. Si queremos que se haga desde la última versión de desarrollo, seleccionamos la opción HEAD. Por último podemos indicar un comentario, por ejemplo, “Creación nueva rama”.
A partir de este momento la copia de trabajo en eclipse pasa a ser la de la rama (si no es así podemos pulsar sobre el proyecto con el botón derecho del ratón y pulsar sobre Team -> Switch to another Branch/tag/revision… para seleccionar la nueva rama), por lo que cualquier modificación que realicemos y subamos al control de versiones se hará sobre ésta. Se harán las modificaciones oportunas en el desarrollo de nuevas funcionalidades y las subiremos al repositorio haciendo commit.

Mantener sincronizada una rama

De forma periódica es conveniente actualizar esta rama con los posibles cambios introducidos en el trunk, ya que aunque no hemos terminado con nuestro trabajo en la rama, deseamos que los cambios en el trunk no se aparten mucho del contenido de la rama, y la sincronización entre ellas sea menos traumática. Ya dijimos que cada rama tiene una línea de desarrollo independiente.

Para realizar un merge del trunk sobre nuestra rama pulsamos sobre el proyecto y vamos a Team -> Merge. Aparecerá a continuación una pantalla en la que debemos seleccionar el modo en el que haremos merge:

Mantener sincronizada una rama

Seleccionamos la primera opción “Merge a range of revisions” para incorporar los cambios en el trunk o en otra rama. Deberemos elegir a continuación la localización de los elementos a sincronizar, y como ya habíamos dicho lo vamos a realizar desde el trunk. En la siguiente pantalla podemos especificar las opciones del proceso de merge.

Ya que se han hecho cambios en el trunk mientras nosotros trabajábamos en nuestra rama, pueden aparecer conflictos. La pantalla siguiente nos avisa de que hay un conflicto en una clase que debemos resolver. Para resolverlo elegimos la opción Launch a graphical conflict resolution editor y se abrirá un editor gráfico con ambas versiones y los cambios señalizados de forma gráfica.

Resolución de conflicto al hacer merge ramas

Al solucionar manualmente el conflicto y cerrar el editor aparece una pantalla de confirmación de resolución del conflicto. Seleccionamos Yes y pulsamos ok.

Al terminar todo el proceso nos muestra una pantalla de resumen. Ya tenemos en una copia local los cambios del trunk y de la rama. Ahora tenemos que consolidar los cambios a la rama haciendo commit y subiendo las modificaciones al control de versiones (dentro de la rama).

Resumen merge ramas Subversion

Reintegrar una rama

Qué ocurre cuando has finalizado tu trabajo en la nueva línea de desarrollo. Tu nueva funcionalidad está acabada y está preparada para pasar al trunk, por lo que debemos cambiar la rama asociada al proyecto pulsando sobre el proyecto con el botón derecho del ratón y seleccionar Team -> Switch to another Branch/tag/revision… para asociar al trunk.

A continuación hacemos un merge, como ya indicamos en el punto anterior, ahora con la opción Reintegrate a branch.

Reintegrar una rama al trunk de subversion

En la pantalla siguiente seleccionamos la rama desde la que vamos a sincronizar. El proceso posterior es similar a lo comentado en el punto anterior, sin olvidar que la sincronización se realiza sobre la copia local y que debemos consolidar los cambios haciendo commit.

Evidentemente todo el proceso comentado aquí puede diferir de las necesidades de cada proyecto: puede interesar hacer merge de unos pocos paquetes, algunos módulos, o de ciertas revisions, en vez del HEAD. En el mundo real pueden surgir falsos conflictos en la sincronización, errores de versiones, que aprendemos a manejar con la práctica y experiencia. También tenemos que tener en cuenta que las versiones del cliente subversión y del servidor son compatibles (sobre todo al reintegrar una rama en el trunk puede dar algún tipo de problema). Lo que está claro es que dominar este proceso nos puede facilitar la vida en ciertos proyectos donde concurren desarrollos en paralelo de diferentes módulos.

Versiones

Eclipse Galileo Build id: 20100218-1602

Subversion Client Adapter: 1.6.10

Subclipse Integration: 3.0.0

Eclipse Mylin: 3.3.2