...
Compare to a simple camel project, the spring beans tag has been enriched with new namespaces :
- spring-osgi (http://www.springframework.org/schema/osgi
...
- ) which is used by OSGI blueprint services
- camel-osgi (http://camel.apache.org/schema/osgi
...
- ) who will allow to integrate camel with OSGI world
- cxf (http://camel.apache.org/schema/cxf
...
- ) to integrate CXF architecture with Camel
Now, that the schema/namespaces are declared, we can start to add addtional additional stuffs like import resources, beans reference, ... that our routing engine will use.
Step 1
...
: Webservice infrastructure : CXF
We will use the CXF framework to deploy the reportincident webservice and run it into the OSGI platform.
...
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)
Step 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
...
All the infrastructure is in place, so we can start to describe the beans that we will use
Step 3
...
: Beans
...
references
5 beans will be used by our application :
...
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>
...
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
Step 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
This first route will be used by Apache Camel to read files deposited in the directory. Here is the explanation of the route
Code Block | ||||
---|---|---|---|---|
| ||||
<camel:camelContext trace="true" xmlns="http://camel.apache.org/schema/osgi"> (1)
<camel:route>
<camel:from uri="file://d:/temp/data/reportincident/?move=d:/temp/backup/${date:now:yyyyMMdd}/${file:name.noext}.bak"/> (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) - camel:camelContext tag is used to instantiate the camelcontext at the launch of the bundle. The trace parameter is defined as true so the tracing will be available on the console
(2) - The from uri="file" informs Camel that a file component must be started and it will listen for incoming files deposited in the directory d:/temp/data/reportincident
. When the file is processed (end of the Camel route), then it is moved to the directory d:/temp/backup/
where the file is renamed (.bak extension is added). During the process of the file and till the route is not finished, the file will be locked
(3) - A header is added to the message with the property origin
setted to file
(4) - To parse the content of the CSV file into a collection of incident objects, we use the action unmarshall
where the reference provided corresponds to the bean BindyCsvDataFormat instantiated by Spring
(5) - The result of the parsing process is copied as a message in the queue:in
From Webservices to queue
The second route will read incoming web services call, extract the XML messages from the web services, transform it into an InputReportIncident, send the object to a bean who will convert it into an Incident object and put the result in the queue:in.
Remarks :
(1) -
(2) -
(3) -
(4) -
(5) -
...
Code Block | ||||
---|---|---|---|---|
| ||||
... <camel:route> <camel:from uri="cxf:bean:reportIncident" /> (1) <camel:setHeader headerName="origin"> (2) <camel:constant>webservice</camel:constant> </camel:setHeader> <camel:convertBodyTo type="org.apache.camel.example.reportincident.InputReportIncident" /> (3) <camel:to uri="bean:webservice" /> (4) <camel:inOnly uri="queuingservice:queue:in" /> (5) <camel:transform>transform> (6) <camel:method bean="feedback" method="setOk" /> </camel:transform> </camel:route> ... |
Remarks :
(1) - The CXF component will be used by Camel to receive incoming web services calls
(2) - A header is added to the message with the property origin
setted to webservice
(3) -
(4) -
(5) -
...
The content of the SOAP envelop is extract and the XML messages is mapped to the InputReportIncident
class
(4) - A message contaning the header, InputReportIncident object is send the bean "Webservice" where the object will be transformed into an Incident
object
(5) - The message is placed into a queue which is a inOnly so no OUT message will be send back to the bean webservice
(6) - A transform process is added to send back to the web services the reply. Using camel:method, we can define which method of the class to use
From queue to DB
The last route will read messages of the queue and send them to the bean IncidentSaver
to save the incidents in the database
Code Block | ||||
---|---|---|---|---|
| ||||
... <camel:route> <camel:from uri="queuingservice:queue:in" /> <camel:to uri="bean:incidentSaver?method=process" /> </camel:route> </camelContext> |
Remarks :
(1) -
(2) -
(3) -
(4) -
(5) -
Add instructions to generate MANIFEST.MF file
Now that the reportincident.routing project is ready, we will modify the pom.xml file to add required instructions to generate the jar and MANIFEST.MF file of the bundle Once that you have finished to configure camel, created your classes, the pom.xml file must be modified :
Code Block | ||||
---|---|---|---|---|
| ||||
... <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;version="[2.0,2.2)", org.apache.camel.component;version="[2.0,2.2)", org.apache.camel.component.cxf;version="[2.0,2.2)", org.apache.camel.component.cxf.converter;version="[2.0,2.2)", org.apache.camel.component.jms.component.jms;version="[2.0,2.2)", org.apache.camel.converter;version="[2.0,2.2)", org.apache.camel.converter.jaxp;version="[2.0,2.2)", org.apache.camel.converter.stream;version="[2.0,2.2)", org.apache.camel.dataformat.bindy;version="[2.0,2.2)", org.apache.camel.dataformat.bindy.csv;version="[2.0,2.2)", org.apache.camel.example.reportincident, org.apache.camel.example.reportincident.model, org.apache.camel.example.reportincident.service, org.apache.camel.processor;version="[2.0,2.2)", 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.jmshttp_osgi, org.apache.cxf.transportstransport.httpjms, org.apache.cxf.transports.workqueuehttp, org.apache.cxf.wsdlworkqueue, org.apache.cxf.wsdl11wsdl, org.apache.servicemix.cxf.transport.http_osgiwsdl11, 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
TODOIn this section of the tutorial, we have discussed how to design the routing between endpoints/components of our application using Camel Spring DSL language. We have also investigated how to setup the infrastructure required to work with ActiveMq, any other queuing engine and CXF. In the next chapter, we will see how to create the web application, package the solution and deploy it on ServiceMix.
Links
...
...
...
- 2b : add infrastructure and routing
- Part
...
#Resources
Attachments patterns .*part2.zip