ServiceMix, Spring

spring y apache servicemix embebido.

Después de un largo tiempo de no publicar, he decidido compartir este articulo sobre como integrar Spring con Apache ServiceMix,no voy a ahondar mucho sobre el framework y el ESB, googleando por la red,no encontré mucha información acerca de como integrar estas dos tecnologías, mas que en la mismo sitio de serviceMix, donde existe un articulo que nos indica por que usar Spring y como podemos configurar nuestro propio fichero xml para nuestros componentes.

Presentación ServiceMix


View more presentations from Jose Bovet.

¿Por  que el articulo?

Uno de los inconvenientes que encontré al momento de empezar a trabajar con apache servicemix, fue el problema con los schemas,  tags, y beans; no se  si es buena practica estar acostumbrado al editor que trae el STS ,pero me facilita mucho el trabajo al configurar los beans de spring, no fue así cuando intente trabajar con los tags de servicemix, ya que de partida me daba errores al encontrar los schemas en el sitio de apache,  fue todo un caos… el tener que aprenderme la estructura que necesita el archivo de servicemix, mmm no tuve mucho tiempo, así que el objetivo fue pasar aquella configuración, a  una mas natural de spring, para esto tome el siguiente ejemplo desde el sitio de apache servicemix,  a grandes rasgos, la idea es tomar un archivo desde la carpeta inbox, moverlo y cambiarle el nombre, siempre y cuando se un archivo con formato xml.

¿Como funciona?

1.  El componente filePoller busca un archivo en el directorio inbox cada 1000 ms.
2.  Una vez que el archivo es encontrado en el directorio, el filePoller consigue un thread desde el manejador de tareas. El thread    se utiliza para procesar el archivo.
3. El filePoller crea un mensaje normalizado que contiene el archivo que sera transmitido. Se envía el mensaje normalizado al NMR.
El NMR routea el mensaje hasa el componente filesender.
4. El fileSender transforma el mensaje normalizado de nuevo a un archivo y lo coloca en el directorio outbox.

Lo primero que voy a hacer es crear un proyecto maven, para ello me voy al STS(Spring Tool Suite).

File->New->Other->Maven Project

Le damos next y nos  pedira el nombre y la ubicación del proyecto,  le damos siguiente donde seleccionaremos que tipo de archetype de maven queremos; para este caso utilizaremos el servicemix-embedded-simple

Luego nos preguntara el groupId y el artifactId, lo completamos y damos finarlizar.Nos creara un proyecto con la siguiente estructura.

Una clase Main y un archivo llamado servicemix.xml como lo muestra la figura anterior.

Ahora crearemos dos carpetas en la raiz del proyecto, una llamada inbox, y outbox, también un Spring Bean Configuration File dentro de la carpeta resources, lo llamare context-jbi.xml y sera donde alojare la configuración de los beans.

Servicemix nos provee la opción y el soporte de Spring, para lo cual, dispone de una clase llamada SpringJBIContainer , este  contenedor JBI, añade algunos métodos de ayuda para facilitar la configuración a través de archivos XML  de spring, teniendo en cuenta esto, vamos a crear un bean de esa clase.

<bean id="jbi" class="org.apache.servicemix.jbi.container.SpringJBIContainer">
   <property name="embedded" value="true" />
   <property name="activationSpecs">
    <list>
      <ref bean="activationSpecFileSender"/>
      <ref bean="activationSpecFilePoller"/>
    </list>
   </property>
 </bean>

Establecemos que el modo sera embebido, y le pasamos como referencia los activationSpecs

	<bean id="activationSpecFileSender" class="org.apache.servicemix.jbi.container.ActivationSpec">
		<property name="componentName" value="fileSender" />
		<property name="service" ref="fileSenderService" />
		<property name="component">
			<bean class="org.apache.servicemix.components.file.FileWriter">
				<property name="directory" value="${OUTBOX}" />
				<property name="marshaler">
					<bean class="org.apache.servicemix.components.util.DefaultFileMarshaler">
						<property name="fileName">
							<bean
								class="org.apache.servicemix.expression.JaxenStringXPathExpression">
								<constructor-arg value="concat('sample_', /sample/@id, '.xml')" />
							</bean>
						</property>
					</bean>
				</property>
			</bean>
		</property>
	</bean>

Necesitamos crear el componente que buscara el archivo en el directorio inbox, para esto seteamos el nombre del componente, el servicio que hace referencia  al bean del servicio llamado fileSenderService, y creamos el componente que sera utilizado, en este caso unFileWriter.

Ahora crearemos el componente que deja el archivo en la carpeta outbox,steamos el nombre del componente, el servicio, el componente FilePoller, y el destinationService, el cual nos dice donde se routeara , en este caso al fileSenderService

	<bean id="activationSpecFilePoller" class="org.apache.servicemix.jbi.container.ActivationSpec">
		<property name="componentName" value="filePoller" />
		<property name="destinationService" ref="fileSenderService" />
		<property name="service" ref="filePollerService" />
		<property name="component">
			<bean class="org.apache.servicemix.components.file.FilePoller">
				<property name="file" value="${INBOX}" />
				<property name="period" value="1000" />
			</bean>
		</property>
	</bean>

Aquí tenemos los service beans de la clase QName, que en este caso no necesitamos pasarle la definición del xml.

   <bean id="fileSenderService" class="javax.xml.namespace.QName">
       <constructor-arg value="fileSender"/>
   </bean>

   <bean id="filePollerService" class="javax.xml.namespace.QName">
       <constructor-arg value="filePoller"/>
   </bean>

Y para terminar un BeanFactoryPostProcessors, el propertyConfigurer para setear las propiedades en las variables ${INBOX} ${OUTBOX}

	<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="location" value="classpath:rutas.properties" />
	</bean>

Y el rutas.properties

INBOX=/media/A880A33480A307C0/WS_INV/ServiceMixEmbebbed/inbox
OUTBOX=/media/A880A33480A307C0/WS_INV/ServiceMixEmbebbed/outbox

Ahora vamos a la clase Main, donde tendremos que cambiar el archivo a leer en el ClassPathXmlApplicationContext ,por defecto fue creado con servicemix.xml lo cambiamos y leemos el context-jbi.xml quedando la clase de la siguiente manera.

import org.apache.servicemix.jbi.container.SpringJBIContainer;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// This is a very simple example of how you might embed ServiceMix
		try {
			final ApplicationContext context = new ClassPathXmlApplicationContext("context-jbi.xml");
			SpringJBIContainer container = (SpringJBIContainer) context.getBean("jbi");

            container.onShutDown(new Runnable() {
                public void run() {
                    if (context instanceof DisposableBean) {
                        try {
                            ((DisposableBean) context).destroy();
                        } catch (Exception e) {
                            System.out.println("Caught: " + e);
                            e.printStackTrace();
                        }
                    }
                }
            });
		} catch (Exception e) {
			System.out.println("Caught: " + e);
			e.printStackTrace();
		}

	}

}

Como vemos, ya ha quedado nuestro bean, solo falta agregar a las dependencias de maven el servicemix-components

Dejas un archivo xml en la carpeta inbox, y corres la clase main, eso es todo… como ves pasamos de la engorrosa configuracion de service mix del ejemplo

<?xml version="1.0" encoding="UTF-8"?>
<!--

    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
    this work for additional information regarding copyright ownership.
    The ASF licenses this file to You under the Apache License, Version 2.0
    (the "License"); you may not use this file except in compliance with
    the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

-->
<beans xmlns:sm="http://servicemix.apache.org/config/1.0"
	     xmlns:foo="http://servicemix.org/demo/">

  <bean id="jndi" class="org.apache.xbean.spring.jndi.SpringInitialContextFactory"
        factory-method="makeInitialContext" singleton="true" />

	<!-- the JBI container -->
	<sm:container id="jbi" useMBeanServer="true" createMBeanServer="true">

		<sm:activationSpecs>

			<!-- Write files to the outbox directory -->
			<sm:activationSpec componentName="fileSender"
				service="foo:fileSender">
				<sm:component>
					<bean class="org.apache.servicemix.components.file.FileWriter">
						<property name="directory" value="outbox" />
						<property name="marshaler">
							<bean
								class="org.apache.servicemix.components.util.DefaultFileMarshaler">
								<property name="fileName">
									<bean
										class="org.apache.servicemix.expression.JaxenStringXPathExpression">
										<constructor-arg
											value="concat('sample_', /sample/@id, '.xml')" />
									</bean>
								</property>
							</bean>
						</property>
					</bean>
				</sm:component>
			</sm:activationSpec>

			<!-- Look for files in the inbox directory -->
			<sm:activationSpec componentName="filePoller"
				destinationService="foo:fileSender" service="foo:filePoller">
				<sm:component>
					<bean class="org.apache.servicemix.components.file.FilePoller">
						<property name="file" value="inbox" />
						<property name="period" value="1000" />
					</bean>
				</sm:component>
			</sm:activationSpec>
		</sm:activationSpecs>
	</sm:container>

</beans>

a algo mas natural 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:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">

	<bean id="jbi" class="org.apache.servicemix.jbi.container.SpringJBIContainer">
		<property name="embedded" value="true" />
		<property name="activationSpecs">
			<list>
				<ref bean="activationSpecFileSender"/>
				<ref bean="activationSpecFilePoller"/>
			</list>
		</property>
	</bean>

	<bean id="activationSpecFileSender" class="org.apache.servicemix.jbi.container.ActivationSpec">
		<property name="componentName" value="fileSender" />
		<property name="service" ref="fileSenderService" />
		<property name="component">
			<bean class="org.apache.servicemix.components.file.FileWriter">
				<property name="directory" value="${OUTBOX}" />
				<property name="marshaler">
					<bean class="org.apache.servicemix.components.util.DefaultFileMarshaler">
						<property name="fileName">
							<bean
								class="org.apache.servicemix.expression.JaxenStringXPathExpression">
								<constructor-arg value="concat('sample_', /sample/@id, '.xml')" />
							</bean>
						</property>
					</bean>
				</property>
			</bean>
		</property>
	</bean>

	<bean id="activationSpecFilePoller" class="org.apache.servicemix.jbi.container.ActivationSpec">
		<property name="componentName" value="filePoller" />
		<property name="destinationService" ref="fileSenderService" />
		<property name="service" ref="filePollerService" />
		<property name="component">
			<bean class="org.apache.servicemix.components.file.FilePoller">
				<property name="file" value="${INBOX}" />
				<property name="period" value="1000" />
			</bean>
		</property>
	</bean>

   <bean id="fileSenderService" class="javax.xml.namespace.QName">
       <constructor-arg value="fileSender"/>
   </bean>

   <bean id="filePollerService" class="javax.xml.namespace.QName">
       <constructor-arg value="filePoller"/>
   </bean>

	<bean id="propertyConfigurer"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="location" value="classpath:rutas.properties" />
	</bean>

</beans>

Eso es por el momento, intentare subir el código fuente del proyecto maven a Git esta semana.

Código disponible aqui

Muchas gracias por darte el tiempo de leer.

Estándar