Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

1) Declare the project in the maven pom.xml file as of type <packaging>bundle</packaging>,
2) Add the maven felix plugin who will generate the MANIFEST.MF file,
3) Identify the packages to be imported/exported and version,
4) Identify and register OSGI services when they are used by another bundle.Remark :

Tip

Spring team has created the Spring Dynamic Modules project to facilitate the work of the developer working with OSGI specification. It allows to transform existing spring beans (interface + implemented class) into OSGI services reachable by any bundle deployed. In fact the services are declared in a repository under the form of interfaces. This mechanism is known now as blueprint services or RFC 124 and is currently integrated under the OSGI specification R4.2.

Step 1

...

: reportincident.model

To transform the reportincident.model project, we will execute the steps 1) 2) and 3) because no services must be registered for this project. So, update the pom.xml file created and add/change what is put in the code below in the comment <!- STEP ->XML comment with word STEP

code
Code Block
xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<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>org.apache.camel.example</groupId>
	<artifactId>reportincident.model</artifactId>

        <!-- STEP 1 -->
	<packaging>bundle</packaging>

	<name>Report Incident Model Bundle</name>
	<version>1.0-SNAPSHOT</version>

	<properties>
		<felix-version>1.4.3</felix-version>
		<camel-version>2.0-M1</camel-version>
		<commons-lang>2.4</commons-lang>
	</properties><parent>
            <groupId>org.apache.camel.example</groupId>
	    <artifactId>reportincident.parent</artifactId>
	    <version>1.0-SNAPSHOT</version>
	</parent>

	<dependencies>
		<!-- Camel bindy  -->
		<dependency>
			<groupId>org.apache.camel</groupId>
			<artifactId>camel-bindy</artifactId>
			<version>${camel-version}</version>
		</dependency>
		<dependency>
			<groupId>commons-lang</groupId>
			<artifactId>commons-lang</artifactId>
			<version>${commons-lang}</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<!-- to compile with 1.5 -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.5</source>
					<target>1.5</target>
				</configuration>
			</plugin>

                        <!-- STEP 2 -->
			<!-- to generate the MANIFEST-FILE of the bundle -->
			<plugin>
				<groupId>org.apache.felix</groupId>
				<artifactId>maven-bundle-plugin</artifactId>
				<extensions>true</extensions>
				<version>${felix-version}</version>
				<configuration>
					<manifestLocation>META-INF</manifestLocation>
					<instructions>
						<Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>

                                                <!-- STEP 3 -->
						<Export-Package>
						    '=META-INF.org.apache.camel.example.reportincident.model',
						    org.apache.camel.example.reportincident.model
						</Export-Package>

                                                <_failok>true</_failok>
					</instructions>
				</configuration>
			</plugin>

		</plugins>
	</build>
</project>

...

Code Block
mvn clean install org.ops4j:maven-pax-plugin:eclipse
Tip

...

the goal org.ops4j:maven-pax-plugin:eclipse is added to refresh the MANIFEST.MF file created

This command will generate a jar file containing the classes, hibernate file and MANIFEST.MF file and deploy it in your maven local repository. You can open the jar and check if the the content of MANIFEST.MF is similar to

...

It is time to continue with the persistence project where we will introduce new important concepts

Step 2

...

: reportincident.persistence

First, you have to replace the pom.xml file created with the file provided in the project attached (see resource). If you open this file, you will see that the <Import-Package> section of the maven-felix-plugin has been enriched with the packages required to work with Hibernate, Spring and JTA classes.

Nevertheless, it is interesting to mention that we have exported the package org.apache.camel.example.reportincident.dao and defined org.apache.camel.example.reportincident.dao.impl as private. Why, the reason is very simple. We , we would like to export only the interface to another 'service' bundles and keep internally the implementation.

Code Block
xml
xml
<Private-Package>org.apache.camel.example.reportincident.dao.impl</Private-Package>
    <Export-Package>org.apache.camel.example.reportincident.dao</Export-Package>

...

In order to test the tip, update your pom.xml with the following info :

Code Block
xml
xml
<Import-Package>
	META-INF.org.apache.camel.example.reportincident.model,
	com.mysql.jdbc,
	org.apache.camel.example.reportincident.model,
	org.apache.commons.dbcp,
	*
    </Import-Package>
    <Private-Package>
	org.apache.camel.example.reportincident.dao.impl
    </Private-Package>
    <Export-Package>
	org.apache.camel.example.reportincident.dao
    </Export-Package>
    <DynamicImport-Package>*</DynamicImport-Package>

...

Create the file persistence-osgi.xml in the directory src/main/resources/META-INF/spring and add the lines :

Code Block
xml
xml
<?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:osgi="http://www.springframework.org/schema/osgi"
  xsi:schemaLocation="
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/osgi
      http://www.springframework.org/schema/osgi/spring-osgi.xsd">

    <osgi:service ref="incidentDAO" interface="org.apache.camel.example.reportincident.dao.IncidentDAO"/>

</beans>

...

Another feature that I would like to introduce here concerns the Configuration Admin. In its simplest form, the CM can be seen as a configuration source, namely a Dictionary whose keys are always Strings. Spring DM can expose entries in the CM as a Properties object, through the cm-properties element. A minimal declaration looks as follows:

...

.

So, we can adapt our spring-datasource-beans.xml file created in the previous chapter with new xml tags :

Code Block

    <context:property-placeholder properties-ref="preProps" />	 (1)
    ...
    <osgix:cm-properties id="preProps" persistent-id="org.apache.camel.example.reportincident.datasource

...

Remarks :

  • If the file is not found, the properties declared in the spring xml file will be used.
  • In our example, we have only defined properties for the driver, username and {{password }} but you can extend the list of values with by example Hibernate parameters like hibernate.show_sql, hibernate.format_sql, ...

3) reportincident.service

Like for the project reportincident.persistence, we will replace our pom.xml file with the one provided in the zip file. As you can see in the <Import-Package>, we will import here the class required by the service : org.apache.camel.example.reportincident.dao

Adding this line in the Import-Package is not enough to have access to the OSGI service. The file spring-service-beans-dao.xml must be modified to have a reference to this interface through the osgi:reference namespace :

Code Block

...
    <property name="incidentDAO">
        <osgi:reference interface="org.apache.camel.example.reportincident.dao.IncidentDAO"/>
    </property>
...

This reference will be used to call the osgi service to find the service corresponding to the interface name declared. If a match occurs, then a spring bean reference is created in the bundle reportincident.service.

To expose our service as an OSGI service, we will create the file service-osgi.xml in the directory src/main/resources/META-INF/spring and add the code.

Code Block

<?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:osgi="http://www.springframework.org/schema/osgi"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
                      http://www.springframework.org/schema/beans/spring-beans.xsd
                      http://www.springframework.org/schema/osgi
                      http://www.springframework.org/schema/osgi/spring-osgi.xsd">

  <osgi:service ref="incidentService" interface="org.apache.camel.example.reportincident.service.IncidentService"/>
                
</beans>

Remark :

  • Transaction Management has been defined in the corresponding files of reportincident.persistence and reportincident.service but we will not discuss them in detail in this tutorial.

4) reportincident.webservice

This bundle will not be exported as an OSGI service. So, we only need to modify the content of <Export-Package> to export the classes generated by the wsl2java maven plugin and the wsdl file :

Code Block

    <Export-Package>
	org.apache.camel.example.reportincident,
     	'=META-INF.wsdl'
    </Export-Package>

Now that we have transformed our current project in bundles, it is time to design the routing and web parts of the application.

Routing/Mediation service

The routing/mediation between services/bundles will be created using Camel Spring DSL language. We will describe its creation/genesis step by step.

First, create the file camel-context.xml in the directory src/main/resources/META-INF/spring and add the lines :

...


<?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:camel="http://camel.apache.org/schema/spring"
	xmlns:osgi="http://www.springframework.org/schema/osgi"
	xmlns:cxf="http://camel.apache.org/schema/cxf"
	xsi:schemaLocation="
	    http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
		http://www.springframework.org/schema/osgi
		http://www.springframework.org/schema/osgi/spring-osgi.xsd
		http://camel.apache.org/schema/osgi
		http://camel.apache.org/schema/osgi/camel-osgi.xsd
		http://camel.apache.org/schema/spring
		http://camel.apache.org/schema/spring/camel-spring.xsd
		http://camel.apache.org/schema/cxf
		http://camel.apache.org/schema/cxf/camel-cxf.xsd">
</beans>

Compare to a simple camel project, the spring beans tag has been enriched with new namespaces :

Now, that the schema/namespaces are declared, we can start to add addtional stuffs like import resources, beans reference, ... that our routing engine will use.

1) Webservice infrastructure : CXF

We will use the CXF framework to deploy the reportincident webservice and run it into the OSGI platform.

To work with it, resources (= spring beans) declared in CXF project must be imported using the statement import resource.

...


...
 <import resource="classpath:META-INF/cxf/cxf.xml" />
 <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
 <import resource="classpath:META-INF/cxf/cxf-extension-http.xml" />
 <import resource="classpath:META-INF/cxf/osgi/cxf-extension-osgi.xml" /> 
...

These imports will be used by spring at the bundle startup to instantiate the beans defined in these files. These beans are responsible in fact to deploy the architecture of the CXF bus top of the OSGI server and to provide a servlet that we will use to communicate with webservices engine of CXF.

Remark : for the purpose of this tutorial, we have packaged this configuration into the camel-spring file but it could be defined in a separate xml file with by example the component bean that Camel will use to communicate with CXF bus. This allows you to separate routing from parameters to be provided to configure endpoints.

The camel CXF endpoint is configurated like this :

...


...
	<!-- webservice endpoint --> 
	<cxf:cxfEndpoint id="reportIncident"
		address="/camel-example/incident" (1)
		serviceClass="org.apache.camel.example.reportincident.ReportIncidentEndpoint" (2)
		xmlns:s="http://reportincident.example.camel.apache.org"> (3)
	</cxf:cxfEndpoint>
...

Remarks :
(1) - the address corresponds to the URI address of the web services,
(2) - the serviceClass is the name of the class used work with the webservices and deployed in the bundle reportincident.webservice
(3) - xmlns:s is the namespace of the reportincident webservice (see reportincident.webservice)

2) Queuing engine

No matter if the incidents come from a webservice or a files but before to process and save them in the database, we will put
our messages in a queue. The queue manager used here is ActiveMQ.
Like CXF, we will use spring xml file to deploy ActiveMq into the server and configure it. This will be done in two steps

a) ActiveMQ

Like CXF, ActiveMq can be installed in the infrastructure using a spring.xml configuration file. So, create the file activemq-broker.xml in the directory src/main/resources/META-INF/spring and add the following lines.

...


<beans
  xmlns="http://www.springframework.org/schema/beans"
  xmlns:amq="http://activemq.apache.org/schema/core"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:osgi="http://www.springframework.org/schema/osgi"
  xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://activemq.apache.org/schema/core
       http://activemq.apache.org/schema/core/activemq-core.xsd
       http://camel.apache.org/schema/spring
       http://camel.apache.org/schema/spring/camel-spring.xsd
       http://www.springframework.org/schema/osgi
       http://www.springframework.org/schema/osgi/spring-osgi.xsd">

    <!-- Allows us to use system properties as variables in this configuration file -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>

    <broker xmlns="http://activemq.apache.org/schema/core" brokerName="default" dataDirectory="${servicemix.base}/data/activemq/default" useShutdownHook="false">

        <!-- Destination specific policies using destination names or wildcards -->
        <destinationPolicy>
            <policyMap>
                <policyEntries>
                    <policyEntry queue=">" memoryLimit="5mb"/>
                    <policyEntry topic=">" memoryLimit="5mb">
                        <subscriptionRecoveryPolicy>
                            <lastImageSubscriptionRecoveryPolicy/>
                        </subscriptionRecoveryPolicy>
                    </policyEntry>
                </policyEntries>
            </policyMap>
        </destinationPolicy>

        <!-- Use the following to configure how ActiveMQ is exposed in JMX -->
        <managementContext>
            <managementContext createConnector="false"/>
        </managementContext>

        <!-- The store and forward broker networks ActiveMQ will listen to -->
        <networkConnectors>
            <!-- by default just auto discover the other brokers -->
            <networkConnector name="default-nc" uri="multicast://default"/>
            <!-- Example of a static configuration:
            <networkConnector name="host1 and host2" uri="static://(tcp://host1:61616,tcp://host2:61616)"/>
            -->
        </networkConnectors>

        <persistenceAdapter>
            <amqPersistenceAdapter syncOnWrite="false" directory="${servicemix.base}/data/activemq/default" maxFileLength="20 mb"/>
        </persistenceAdapter>

        <!-- Use the following if you wish to configure the journal with JDBC -->
        <!--
        <persistenceAdapter>
            <journaledJDBC dataDirectory="${activemq.base}/data" dataSource="#postgres-ds"/>
        </persistenceAdapter>
        -->

        <!-- Or if you want to use pure JDBC without a journal -->
        <!--
        <persistenceAdapter>
            <jdbcPersistenceAdapter dataSource="#postgres-ds"/>
        </persistenceAdapter>
        -->

        <!--  The maximum about of space the broker will use before slowing down producers -->
        <systemUsage>
            <systemUsage>
                <memoryUsage>
                    <memoryUsage limit="20 mb"/>
                </memoryUsage>
                <storeUsage>
                    <storeUsage limit="1 gb" name="foo"/>
                </storeUsage>
                <tempUsage>
                    <tempUsage limit="100 mb"/>
                </tempUsage>
            </systemUsage>
        </systemUsage>


        <!-- The transport connectors ActiveMQ will listen to -->
        <transportConnectors>
            <transportConnector name="openwire" uri="tcp://localhost:61616" discoveryUri="multicast://default"/>
            <transportConnector name="stomp" uri="stomp://localhost:61613"/>
        </transportConnectors>

    </broker>

    <bean id="activemqConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
        <property name="brokerURL" value="tcp://localhost:61616" />
    </bean>

    <bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactoryBean">
        <property name="maxConnections" value="8" />
        <property name="maximumActive" value="500" />
        <property name="transactionManager" ref="transactionManager" />
        <property name="connectionFactory" ref="activemqConnectionFactory" />
        <property name="resourceName" value="activemq.default" />
    </bean>

    <bean id="resourceManager" class="org.apache.activemq.pool.ActiveMQResourceManager" init-method="recoverResource">
          <property name="transactionManager" ref="transactionManager" />
          <property name="connectionFactory" ref="activemqConnectionFactory" />
          <property name="resourceName" value="activemq.default" />
    </bean>

    <osgi:reference id="transactionManager" interface="javax.transaction.TransactionManager" />

    <osgi:service ref="pooledConnectionFactory">
        <osgi:interfaces>
            <value>javax.jms.ConnectionFactory</value>
        </osgi:interfaces>
        <osgi:service-properties>
            <entry key="name" value="default"/>
        </osgi:service-properties>
    </osgi:service>

</beans>

At the bundle startup, Spring will instantiate the beans declared and in consequence start the queuing engine. We haven't changed the content of the file corresponding to what is proposed in the ServiceMix distribution but you can use here the same technique described for the Datasource and add properties that you configure through by example a org.apache.activemq.config.etc file.

...


    ...
    <transportConnectors>
        <transportConnector name="${name}" uri="${uri}" discoveryUri="${discoveryUri}"/>
    </transportConnectors>
    
    <!-- here is the list of values defined as default but can be overidded in the file org.apache.activemq.config.etc -->
    <osgix:cm-properties id="confs" persistent-id="org.apache.activemq.config.etc">
        <prop key="name">openwire</prop>
        <prop key="uri">tcp://localhost:61616</prop>
        <prop key="discoveryUri">multicast://default</prop>
    </osgix:cm-properties>
    ...
"> (2)
        <prop key="driverClassName">com.mysql.jdbc.Driver</prop>
        <prop key="url">jdbc:mysql:///report</prop>
        <prop key="username"></prop>
        <prop key="password"></prop>        
    </osgix:cm-properties>
    ...
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
	<property name="driverClassName" value="${driverClassName}" /> (3)
	<property name="url" value="${url}" />
	<property name="username" value="${username}" />
	<property name="password" value="${password}" />
    </bean>

The configuration above, exposes the properties available in the CM under org.apache.camel.example.reportincident.datasource entry (2) as a bean named preProps. Moreover, the property declared (3) can be override by creating a file named org.apache.camel.example.reportincident.datasource.cfg and containing the parameters :

Code Block

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///report
username=root
password=

Spring using the context:property-placeholder (1) will be able to load it.

Remarks :

  • We will see in the chapter 'deployment' where this file must be deployed.
  • In our example, we have only defined properties for the driver, username and {{password }} but you can extend the list of values with by example Hibernate parameters like hibernate.show_sql, hibernate.format_sql, ...

Step 3 : reportincident.service

Like for the project reportincident.persistence, we will replace our pom.xml file with the one provided in the zip file. As you can see in the <Import-Package>, we will import here the class required by the service : org.apache.camel.example.reportincident.dao

Adding this line in the Import-Package is not enough to have access to the OSGI service. The file spring-service-beans-dao.xml must be modified to have a reference to this interface through the osgi:reference namespace :

Code Block
xml
xml

...
    <property name="incidentDAO">
        <osgi:reference interface="org.apache.camel.example.reportincident.dao.IncidentDAO"/>
    </property>
...

This reference will be used to call the osgi service to find the service corresponding to the interface name declared. If a match occurs, then a spring bean reference is created in the bundle reportincident.service.

To expose our service as an OSGI service, we will create the file service-osgi.xml in the directory src/main/resources/META-INF/spring and add the code.

Code Block
xml
xml

<?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:osgi="http://www.springframework.org/schema/osgi"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
                      http://www.springframework.org/schema/beans/spring-beans.xsd
                      http://www.springframework.org/schema/osgi
                      http://www.springframework.org/schema/osgi/spring-osgi.xsd">

  <osgi:service ref="incidentService" interface="org.apache.camel.example.reportincident.service.IncidentService"/>
                
</beans>

Remark :

  • Transaction Management has been defined in the corresponding files of reportincident.persistence and reportincident.service but we will not discuss them in detail in this tutorial.

Step 4 : reportincident.webservice

This bundle will not be exported as an OSGI service. So, we only need to modify the content of <Export-Package> to export the classes generated by the wsl2java maven plugin and the wsdl file :

Code Block
xml
xml

    <Export-Package>
	org.apache.camel.example.reportincident,
     	'=META-INF.wsdl'
    </Export-Package>

Conclusion

Now that we have transformed our current project in bundles, it is time to design the routing and web parts of the application. In the next part of the tutorial, we will specify modification to do for the new incoming projects/bundles

Tip

The activeMq broker can also be integrated differently because the ServiceMix4 distribution (and not ServiceMix Kernel) proposes it in standard with additional commands that you can use from the console to :

Available commands in activemq:
browse Display selected messages in a specified destination
bstat Displays useful broker statistics
create-broker Creates a broker instance.
destroy-broker Destroys a broker instance.
list Lists all available brokers in the specified JMX context
purge Delete selected destination's messages that matches the message selector
query Display selected broker component's attributes and statistics

The pom.xml file must be modified to add properties required by Spring blueprint. So add the following lines :

...


...
    <instructions>
      <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
      <DynamicImport-Package>*</DynamicImport-Package>
      <Include-Resource>src/main/resources</Include-Resource> (1)
      <Spring-Context>*;publish-context:=false;create-asynchronously:=true</Spring-Context> (2)
      <Private-Package></Private-Package>
      <Import-Package> (3)
	javax.transaction,
	org.apache.activemq,
        org.apache.activemq.pool,
	org.springframework.beans.factory.config,
	*
       </Import-Package>
    </instructions>
...

Remarks :
(1) - Include-Resource will allow to add to the jar generated the spring files of the directory src/main/resources/META-INF/spring
(2) - The Spring-Context tag allows to provide specific information used by Spring blueprint service to load the application context and how to load it. The asterisk means that all the spring xml files will be processed
(3) - The list of classes to be used is dependent of the queue engine use as implementation. This list must be reviewed when switching from ActiveMq to IBM WebSphere MQ, Tibco, ...

b) Camel ActiveMq component

To makes Camel independent of the JMS queue engine deployed in the OSGI server, we will implement a proxy using blueprint service between Camel component and the queuing engine used (Apache ActiveMq, IBM Websphere MQ, Oracle Advance Queue, TIBCO, ...)

First, create a spring DSL file osgi-queuingservice.xml in the directory {{src/main/resources/META-INF-spring}}directory containing the following information :

...


<?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:osgi="http://www.springframework.org/schema/osgi"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
                      http://www.springframework.org/schema/beans/spring-beans.xsd
                      http://www.springframework.org/schema/osgi
                      http://www.springframework.org/schema/osgi/spring-osgi.xsd
                      http://camel.apache.org/schema/spring
                      http://camel.apache.org/schema/spring/camel-spring.xsd">
      
    <bean id="active-mq" class="org.apache.activemq.camel.component.ActiveMQComponent" /> (1)

    <osgi:service id="osgiqueuingservice" ref="active-mq" interface="org.apache.camel.Component"/> (2)
       
</beans>

Remarks:
(1) Spring will instantiate the ActiveMqComponent to work with the ActiveMq server. If you would like to use another JMS component, then switch this class to org.apache.camel.component.jms.JmsComponent

(2) Our camel component will be exposed on the OSGI registry as an org.apache.camel.Component and has a reference to the ActiveMQComponent, JMSComponent

Next adapt the POM.xml file like this to instruct the felix plugin how to generate the MANIFEST.MF file

...


...
			<plugin>
				<groupId>org.apache.felix</groupId>
				<artifactId>maven-bundle-plugin</artifactId>
				<version>${felix-version}</version>
				<configuration>
					<manifestLocation>META-INF</manifestLocation>
					<instructions>
						<Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
						<Import-Package>
						    org.apache.activemq.camel.component,
						    org.apache.camel,
						    *</Import-Package>
					    <Include-Resource>src/main/resources</Include-Resource>
					    <Spring-Context>*;publish-context:=false;create-asynchronously:=true</Spring-Context>
					    <Private-Package></Private-Package>
					</instructions>
				</configuration>
			</plugin>
...

Remark that we import here the org.apache.activemq.camel.component class and not the ActiveMQComponent java class.

All the infrastructure is in place, so we can start to describe the beans that we will use

3) Beans reference

5 beans will be used by our application :

  • BindyCsvDataFormat : to generate the model, marshall (= parse a CSV file in to incidents objects) or unmarshall (= create a CSV file from incidents objects)
  • IncidentSaver : proxy service who will extract from the body of the message the Incidents objects and call the reportinicident.Service bundle to save in the DB the incidents
  • WebService : service who will receive messages from WebServices, extract them from the body message and transform them into Incident objects
  • Feedback : simple class who will send a message back to the webservice

So, adapt the camel-context.xml file :

...


	<bean id="bindyDataformat" class="org.apache.camel.dataformat.bindy.csv.BindyCsvDataFormat">
		<constructor-arg type="java.lang.String" value="org.apache.camel.example.reportincident.model" /> (1)
	</bean>

	<bean id="incidentSaver" class="org.apache.camel.example.reportincident.internal.IncidentSaver">
		<property name="incidentService">
			<osgi:reference interface="org.apache.camel.example.reportincident.service.IncidentService"/> (2)
		</property>
	</bean>
	
	<bean id="webservice" class="org.apache.camel.example.reportincident.internal.WebService" />
	<bean id="feedback" class="org.apache.camel.example.reportincident.internal.Feedback" />
	
	<osgi:reference id="queuingservice" interface="org.apache.camel.Component" /> (3)

Remarks :

(1) - The name of the package containing the class of the model must be provided as parameter
(2) - We inject into our proxy service using OSGI service reference, the interface IncidentService
(3) - We need also an OSGI service reference to the queue engine using the interface org.apache.camel.Component

We will quickly review the three classes that we will created for our project in the directory org.apache.camel.example.reportincident.internal :

a) IncidentSaver

Code Block

package org.apache.camel.example.reportincident.internal;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.camel.Exchange;
import org.apache.camel.example.reportincident.model.Incident;
import org.apache.camel.example.reportincident.service.IncidentService;

public class IncidentSaver {

	private static final transient Log LOG = LogFactory.getLog(IncidentSaver.class);
	
	private IncidentService incidentService = null;

	public void process(Exchange exchange) throws ParseException {

		int count = 0;

		List<Map<String, Object>> models = new ArrayList<Map<String, Object>>(); (1)
		Map<String, Object> model = new HashMap<String, Object>();

		// Get models from message
		models = (List<Map<String, Object>>) exchange.getIn().getBody(); (2)
		
		// Get Header origin from message
		String origin = (String) exchange.getIn().getHeader("origin"); (3)
		LOG.debug("Header origin : " + origin);

		Iterator<Map<String, Object>> it = models.iterator();
		
		// Specify current Date
                DateFormat format = new SimpleDateFormat( "dd/MM/yyyy HH:mm:ss" );
                String currentDate = format.format( new Date() );
                Date creationDate = format.parse( currentDate );
        
		while (it.hasNext()) {

			model = it.next();
			
			LOG.debug("Model retrieved");

			for (String key : model.keySet()) {
				
				LOG.debug("Object retrieved : " + model.get(key).toString());
				
				// Retrieve incident from model
				Incident incident = (Incident) model.get(key); (4)
				incident.setCreationDate(creationDate);
		                incident.setCreationUser(origin);
		        
				LOG.debug("Count : " + count + ", " + incident.toString());
				
				// Save Incident
				incidentService.saveIncident(incident); (5)
				LOG.debug("Incident saved");
			}

			count++;
		}

		LOG.debug("Nber of CSV records received by the csv bean : " + count);

	}
	
        // Property used to inject service implementation (6)
	public void setIncidentService(IncidentService incidentService) {
		this.incidentService = incidentService;
	}
	
}

Remarks :
(1) - We instantiate List and Map classes that we will use to extract objects of our incident model
(2) - Using the method exchange.getIn().getBody(), we extract the objects from the message and put them in a List
(3) - We get the Header field ('Origin') to check the origin of the messages (file). This info will be persisted in the DB
(4) - The object incident is retrieved from the Map model. In our case, the key is unique because we have only created one model class
(5) - The incident is saved in the database by calling the OSGI service IncidentService.saveIncident
(6) - The field setIncidentService is used by Spring to inject dependency with OSGI service

b) WebService

Code Block

/**
 * 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.
 */
package org.apache.camel.example.reportincident.internal;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.camel.Exchange;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.camel.example.reportincident.InputReportIncident;
import org.apache.camel.example.reportincident.model.Incident;

public class WebService {

	private static final transient Log LOG = LogFactory.getLog(WebService.class);

	public void process(Exchange exchange) throws ParseException {
        
		InputReportIncident webincident = (InputReportIncident)exchange.getIn().getBody(); (1)
		
		LOG.debug("Incident received from : " + webincident.getFamilyName() + ", " + webincident.getGivenName());
		LOG.debug("Incident info : " + webincident.getIncidentId() + ", at : " + webincident.getIncidentDate());
		LOG.debug("Incident details : " + webincident.getDetails() + ", summary : " + webincident.getSummary());
  
		
		List<Map<String, Incident>> models = new ArrayList<Map<String, Incident>>();
		Map<String, Incident> model = new HashMap<String, Incident>();
			
		// Convert the InputReportIncident into an Incident 
		Incident incident = new Incident(); (2)
		
		DateFormat format = new SimpleDateFormat( "dd-mm-yyyy" );
		incident.setIncidentDate(format.parse(webincident.getIncidentDate()));
		
		incident.setDetails(webincident.getDetails());
		incident.setEmail(webincident.getEmail());
		incident.setFamilyName(webincident.getFamilyName());
		incident.setGivenName(webincident.getGivenName());
		incident.setIncidentRef(webincident.getIncidentId());
		incident.setPhone(webincident.getPhone());
		incident.setSummary(webincident.getSummary());
		
		// Get Header origin from message
		String origin = (String) exchange.getIn().getHeader("origin"); (3)

		// Specify current Date
		format = new SimpleDateFormat( "dd/MM/yyyy HH:mm:ss" );
                String currentDate = format.format( new Date() );
                Date creationDate = format.parse( currentDate );
        
                incident.setCreationDate(creationDate);
                incident.setCreationUser(origin);
		
                LOG.debug("Incident created from web service : " + incident.toString());
        
		// and place it in a model (cfr camel-bindy)
		model.put(Incident.class.getName(), incident); (4)
		models.add(model);
		
	        // replace with our input
		exchange.getOut().setBody(models); (5)
		
		// propagate the header
		exchange.getOut().setHeader("origin", origin); (6)
         
     }

}

Remarks :
(1) - The message coming from the WebService and copied in the InputReportIncident class is retrieved from the body using the method exchange.getIn().getBody()
(2) - We create using the model a class Incident where we will put webservice's InputReportIncident
(3) - We get the Header field ('Origin') to check the origin of the messages (webservice). This info will be persisted in the DB
(4) - The model incident is added to the Map and List objects required by Camel Bindy
(5) - The model is added to the body object of the message that we send it OUT
(6) - Thbe header parameter is also propagated for the next endpoint

c) Feedback

Code Block

package org.apache.camel.example.reportincident.internal;

import org.apache.camel.example.reportincident.OutputReportIncident;

public class Feedback {

	public OutputReportIncident setOk() { (3)
		OutputReportIncident outputReportIncident = new OutputReportIncident(); (1)
		outputReportIncident.setCode("0"); (2)
		return outputReportIncident;
	}

}

Remarks :
(1) - An OutputReportIncident object is created because it will be used to send the message back to the webservice
(2) - The field/property setCode is setted with the value ("OK")
(3) - The method setOk() will be called by Camel routing

4) Routing

Now that evverything is in place, we can create the three routes that we need to implement the architecture that we have presented in the introduction of this tutorial

From File(s) to queue

...


	<camelContext trace="true" xmlns="http://camel.apache.org/schema/osgi"> (1)

		<camel:route> 
			<camel:from uri="file://d:/temp/data/?move=d:/temp/done/${file:name}" /> (2)
			<camel:setHeader headerName="origin"> (3)
				<camel:constant>file</camel:constant>
			</camel:setHeader>
			<camel:unmarshal ref="bindyDataformat" /> (4)
			<camel:to uri="queuingservice:queue:in" /> (5)
		</camel:route>
...

Remarks :
(1) -
(2) -
(3) -
(4) -
(5) -

From Webservices to queue

...


...		 
		<camel:route>
			<camel:from uri="cxf:bean:reportIncident" />
			<camel:setHeader headerName="origin">
				<camel:constant>webservice</camel:constant>
			</camel:setHeader>
			<camel:convertBodyTo type="org.apache.camel.example.reportincident.InputReportIncident" />
			<camel:to uri="bean:webservice" />
			<camel:inOnly uri="queuingservice:queue:in" />
			<camel:transform>
				<camel:method bean="feedback" method="setOk" />
			</camel:transform>
		</camel:route>
...

Remarks :
(1) -
(2) -
(3) -
(4) -
(5) -

From queue to DB

...


...		 
	
		<camel:route>
			<camel:from uri="queuingservice:queue:in" />
			<camel:to uri="bean:incidentSaver?method=process" />
		</camel:route>
	</camelContext>

Remarks :
(1) -
(2) -
(3) -
(4) -
(5) -

Once that you have finished to configure camel, created your classes, the pom.xml file must be modified :

...


...
					<instructions>
						<Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
						<Import-Package> (1)
						    META-INF.cxf,
						    META-INF.cxf.osgi,
						    META-INF.wsdl,
						    org.apache.commons.logging,
						    org.apache.camel,
						    org.apache.camel.component,
						    org.apache.camel.component.cxf,
						    org.apache.camel.component.cxf.converter,
						    org.apache.camel.component.jms,
						    org.apache.camel.converter,
						    org.apache.camel.converter.jaxp,
						    org.apache.camel.converter.stream,
							org.apache.camel.dataformat.bindy,
							org.apache.camel.dataformat.bindy.csv,
							org.apache.camel.example.reportincident,
							org.apache.camel.example.reportincident.model,
							org.apache.camel.example.reportincident.service,
							org.apache.camel.processor,
							org.apache.activemq.camel.component;${activemq.osgi.version},
							org.apache.activemq.camel.converter;${activemq.osgi.version},
							org.apache.activemq.pool,
							org.apache.cxf,
							org.apache.cxf.binding,
							org.apache.cxf.binding.corba,
							org.apache.cxf.binding.soap,
							org.apache.cxf.binding.soap.spring,
							org.apache.cxf.bus,
							org.apache.cxf.bus.resource,
							org.apache.cxf.bus.spring,
							org.apache.cxf.buslifecycle,
							org.apache.cxf.catalog,
							org.apache.cxf.configuration,
							org.apache.cxf.configuration.spring,
							org.apache.cxf.endpoint,
							org.apache.cxf.headers,
							org.apache.cxf.management,
							org.apache.cxf.management.jmx,
							org.apache.cxf.phase,
							org.apache.cxf.resource,
							org.apache.cxf.transport,
							org.apache.cxf.transport.http,
							org.apache.cxf.transport.http.policy,
							org.apache.cxf.transport.http_jetty,
							org.apache.cxf.transport.jms,
							org.apache.cxf.transports.http,
							org.apache.cxf.workqueue,
							org.apache.cxf.wsdl,
							org.apache.cxf.wsdl11,
							org.apache.servicemix.cxf.transport.http_osgi,
							org.springframework.beans.factory.config,
							*
					    </Import-Package>
					    <Private-Package>org.apache.camel.example.reportincident.internal</Private-Package> (2)
					</instructions>
...

Remarks :
(1) - Classes required by Camel, CXF muste be imported.
(2) - Our internal classes are declared as private to avoid that they become available for another bundles of the OSGI server

Conclusion

TODO

...

...

  • 2a : transform projects in bundles
  • Part

...

...

#Resources

  • Attachments
    patterns.*part2.zip