Monday, February 3, 2014

Create Apache Camel OSGi Bundle Maven Project From Scratch

There are plenty maven projects which create Apache Camel OSGi bundles. But there is very few place I can find to simply explain how to create such a kind project from scratch. This blog will exmain the details about this process. Frankly speaking, it may seems very simple, but it took me several hours to get it work. Hopefully, people who are interested in this process do not have to go through the same processes as I did.

Create Maven Project with Apach Camel Routes

Here is the script I created for creating maven project with apache camel route [I use cygwin in my windows environment. If you run dos cmd, you can replace the \ with ^ for line continuation.]:

1
2
3
4
5
6
7
mvn archetype:generate \
  -DarchetypeGroupId=org.apache.camel.archetypes \
  -DarchetypeArtifactId=camel-archetype-spring  \
  -DarchetypeVersion=2.10.4 \
  -DgroupId=com.vha.esb.apexus \
  -DartifactId=ce-reject-notify \
  -Dversion=1.0.0-SNAPSHOT
You can put this into a file, namely, generateProject.sh for linux OS, or generateProject.bat. If you run under linux os or cygwind, make sure change the file permission:
1
chmod 755 generatePoject.sh

Now you can run the script, it will generate all files with a defaul apache camel route, which simple copy a file from src/data to target/message/uk or target/message/others. Aparatly, apache camel community did an excellent job to make this process go smoothly. After the build process is completed, run the following commands:

1
2
mvn clean install
mvn camel:run
If you now check the target/messages/uk and target/messages/others, you will find an xml file existing in each directory.

Import Project to Eclipse

The projects created by the running the script is ready to be imported to eclipse IDE.

Create OSGi Bundle

To create OSGi bundle, we need to instruct maven to build bundle using the org.apache.felix maven plugin. Here is my pom.xml file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
<!--xml version="1.0" encoding="UTF-8"?-->
 
 <modelversion>4.0.0</modelversion>
 
 <groupid>com.vha.esb.apexus</groupid>
 <artifactid>ce-reject-notify</artifactid>
 <packaging>bundle</packaging>
 <version>1.0.0-SNAPSHOT</version>
 
 <name>A Camel Spring Route</name>
 <url>http://www.myorganization.org</url>
 
 <properties>
  <project.build.sourceencoding>UTF-8</project.build.sourceencoding>
  <project.reporting.outputencoding>UTF-8</project.reporting.outputencoding>
 </properties>
 
 <dependencies>
  <dependency>
   <groupid>org.apache.camel</groupid>
   <artifactid>camel-core</artifactid>
   <version>2.10.4</version>
  </dependency>
  <dependency>
   <groupid>org.apache.camel</groupid>
   <artifactid>camel-spring</artifactid>
   <version>2.10.4</version>
  </dependency>
 
  <!-- logging -->
  <dependency>
   <groupid>org.slf4j</groupid>
   <artifactid>slf4j-api</artifactid>
   <version>1.6.6</version>
  </dependency>
  <dependency>
   <groupid>org.slf4j</groupid>
   <artifactid>slf4j-log4j12</artifactid>
   <version>1.6.6</version>
  </dependency>
  <dependency>
   <groupid>log4j</groupid>
   <artifactid>log4j</artifactid>
   <version>1.2.17</version>
  </dependency>
 
  <!-- testing -->
  <dependency>
   <groupid>org.apache.camel</groupid>
   <artifactid>camel-test-spring</artifactid>
   <version>2.10.4</version>
   <scope>test</scope>
  </dependency>
 
 </dependencies>
 
 <build>
  <defaultgoal>install</defaultgoal>
 
  <plugins>
   <plugin>
    <groupid>org.apache.felix</groupid>
    <artifactid>maven-bundle-plugin</artifactid>
    <extensions>true</extensions>
    <configuration>
     <manifestlocation>target/META-INF</manifestlocation>
     <instructions>
      <bundle-symbolicname>${project.groupId}.${project.artifactId}</bundle-symbolicname>
      <export-package></export-package>
      <import-package>
       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.service.factory,
       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.ws.security.action,
       org.apache.ws.security.processor,
       org.springframework.beans.factory.config,
       org.springframework.mail.javamail,
       *
      </import-package>
     </instructions>
    </configuration>
   </plugin>
 
 
 
 
 
 
   <plugin>
    <groupid>org.apache.maven.plugins</groupid>
    <artifactid>maven-compiler-plugin</artifactid>
    <version>2.5.1</version>
    <configuration>
     <source>1.6
     <target>1.6</target>
    </configuration>
   </plugin>
   <plugin>
    <groupid>org.apache.maven.plugins</groupid>
    <artifactid>maven-resources-plugin</artifactid>
    <version>2.4.3</version>
    <configuration>
     <encoding>UTF-8</encoding>
    </configuration>
   </plugin>
 
   <!-- allows the route to be ran via 'mvn camel:run' -->
   <plugin>
    <groupid>org.apache.camel</groupid>
    <artifactid>camel-maven-plugin</artifactid>
    <version>2.10.4</version>
   </plugin>
  </plugins>
 </build>
 
</project>
Note: 2 places are changed: one packaging tag. By default, it is jar. Now it is bundle:
1
<packaging>bundle</packaging>
The other place is the plugin:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<plugin>
 <groupid>org.apache.felix</groupid>
 <artifactid>maven-bundle-plugin</artifactid>
 <extensions>true</extensions>
 <configuration>
  <manifestlocation>target/META-INF</manifestlocation>
  <instructions>
   <bundle-symbolicname>${project.groupId}.${project.artifactId}</bundle-symbolicname>
   <export-package></export-package>
   <import-package>
    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.service.factory,
    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.ws.security.action,
    org.apache.ws.security.processor,
    org.springframework.beans.factory.config,
    org.springframework.mail.javamail,
    *
   </import-package>
  </instructions>
 </configuration>
</plugin>

Now we are readly to deploy the bundle to karaf container. The command is:

1
2
osgi:install mvn:com.vha.esb.apexus/ce-reject-notify/1.0.0-SNAPSHOT
osgi:start 244

Now, if your karaf container is at ${karaf_home}, you should see the directory ${karaf_home}/src/data. Now, let's copy the 2 xml files coming with the build to that directory. Shortly, you should see message1.xml and message2.xml are placed at ${karaf_home}/target/messages/uk and ${karaf_home}/target/messages/other directory, respectively.

Understanding Apache Camel Route

The archtetypes: camel-archetype-spring create a default camel route which copy the xml file from one place to anotehr. I add another route using quartz end point as shown blow:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<!--xml version="1.0" encoding="UTF-8"?-->
<!-- Configures the Camel Context -->
 
 
 <bean id="helloBean" class="com.ggl.esb.osgi.HelloBean">
  <property name="say" value="${name.value}">
 </property></bean>
 
 
 <osgix:cm-properties id="preProps" persistent-id="osgi.spring.demo">
 
 </osgix:cm-properties>
 
 <ctx:property-placeholder properties-ref="preProps">
 
 
 
 <camel:camelcontext xmlns="http://camel.apache.org/schema/spring">
  <!-- here is a sample which processes the input files (leaving them in
   place - see the 'noop' flag) then performs content based routing on the message
   using XPath -->
    
  <route id="timerToLog">
            <from uri="quartz://myGroup/propLog?cron=0/5+*+*+*+*+?">
   <setbody>
    <method ref="helloBean" method="hello">
   </method></setbody>
   <log message="The message contains ${body}">
   <to uri="mock:result">
  </to></log></from></route>
    
    
  <camel:route>
   <camel:from uri="file:src/data?noop=true">
   <camel:choice>
    <camel:when>
     <camel:xpath>/person/city = 'London'</camel:xpath>
     <camel:log message="UK message">
     <camel:to uri="file:target/messages/uk">
    </camel:to></camel:log></camel:when>
    <camel:otherwise>
     <camel:log message="Other message">
     <camel:to uri="file:target/messages/others">
    </camel:to></camel:log></camel:otherwise>
   </camel:choice>
  </camel:from></camel:route>
   
   
 </camel:camelcontext>
 
</ctx:property-placeholder></beans>

The update route shows few things. First, it is a spring configuration file. We can define beans inside the configuraion. Secondly, we can define multiple routes within a camelContext.

The camel configuration file shown above is OK, but not very practical because we hard-coded thte value. Now, i want to explain how to use property files. To use property file, we need to update the came-context.xml file. Here is the list of the file content.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<!--xml version="1.0" encoding="UTF-8"?-->
<!-- Configures the Camel Context -->
 
 
       ......
 
 <bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="location" value="file:etc/osgi.spring.demo.cfg">
 </property></bean>
 
 
 
 <camel:camelcontext xmlns="http://camel.apache.org/schema/spring">
  <!-- here is a sample which processes the input files (leaving them in
   place - see the 'noop' flag) then performs content based routing on the message
   using XPath -->
   
  <propertyplaceholder id="properties" location="file:etc/osgi.spring.demo.cfg">
    
  <route id="timerToLog">
            <from uri="quartz://myGroup/propLog?cron={{cron.value}}">
   <setbody>
    <method ref="helloBean" method="hello">
   </method></setbody>
   <log message="The message contains ${body} {{activemq.broker.url}}">
   <to uri="mock:result">
  </to></log></from></route>
    
         ......
   
   
 </propertyplaceholder></camel:camelcontext>
 
</beans>

As you can see, we use spring PropertyPlaceholderConfigurer bean to load file. and in the camel route you use {{my.value}}, where my.value is a variable. I deployed the file osgi.spring.demo.cfg under ${KARAF.HOME}/etc. The ${KARAF.HOME} is the karaf container installation. The file osgi.spring.demo.cfg has the following contents:

1
2
3
activemq.broker.url=failover:(tcp://uit-amq-1.corp.vha.com:61616,tcp://uit-amq-2.corp.vha.com:61616)
name.value=Gary Liu
cron.value=0/5+*+*+*+*+?

After my new deployment of the bundle, in the karaf log, I can see the following out put every 5 seconds, because in the quartz end point, I have defined the field for second 0/5, which is every 5 seconds.

14:48:50,003 | INFO  | amel-21_Worker-2 | rg.apache.camel.util.CamelLogger  176 | 174 - org.apache.camel.camel-core - 2.10.4 | The message contains Gary Liu at 2014-02-12 14:48:50 failover:(tcp://uit-amq-1.corp.vha.com:61616,tcp://uit-amq-2.corp.vha.com:61616)
14:48:55,001 | INFO  | amel-21_Worker-3 | rg.apache.camel.util.CamelLogger  176 | 174 - org.apache.camel.camel-core - 2.10.4 | The message contains Gary Liu at 2014-02-12 14:48:55 failover:(tcp://uit-amq-1.corp.vha.com:61616,tcp://uit-amq-2.corp.vha.com:61616)

Conclusion

Basically, to create Apache Camel route OSGi bundles from scratch, you can simply use the org.apache.camel.archetypes archetype to generate a project. Then you use the apache felix maven plugin to generate OSGi bundle.

No comments:

Post a Comment

Anypoint Studio Error: The project is missing Munit lIbrary to run tests

Anypoint Studio 7.9 has a bug. Even if we following the article: https://help.mulesoft.com/s/article/The-project-is-missing-MUnit-libraries-...