Part 5
... Continued from Part 4
We continue from part 4 where we have the routing in place. However as you might have noticed we aren't quiet there yet with a nice solution, we are still coding to much. In this part we will look into to address these two concerns:
- Starting Camel automatically
- Using CXF
...
- directly
Starting Camel automatically
Our current deployment model is as a war and we have the web.xml
to help start things. Well in fact we will leverage Spring to start the world . We use it's ContextListener
...
Code Block | ||||
---|---|---|---|---|
| ||||
<camelContext id="camel" xmlns="http://activemq.apache.org/camel/schema/spring"> ... </camelContext> |
Adding route builder
Now we have Camel integrated but we still need to add our route bulder that we did manually from the javacode as:
...
There are two solutions to this
- using spring bean
- package scanning
Using a spring bean we just declare the route builder using a regular spring bean:
...
Code Block | ||||
---|---|---|---|---|
| ||||
<camelContext id="camel" xmlns="http://activemq.apache.org/camel/schema/spring"> <package>org.apache.camel.example.reportincident</package> </camelContext> |
Using CXF directly
Now we have seen how you can leverage Spring to start Camel, in fact it handles the lifecycle of Camel, so you can say Camel is embedded with Spring in your application.
...
So now we have two spring XML files
- cxf-config.xml
- camel-config.xml
And since cxf-config.xml is dependent on camel-config.xml we need to have correct ordering in our web.xml where we have defined the XML files to load by Spring. So we set the camel-config.xml before the cxf-config.xml so Spring have created the SpringCamelContext and registered it in its registry with the id = camel.
...
Code Block | ||||
---|---|---|---|---|
| ||||
<xs:element name="inputReportIncident"> <xs:complexType name="inputReportIncident"> |
Using CXF endpoint
Okay now we are ready to turn our attention to using CXF directly in Camel routing. So we zap ReportIncidentEndpointImpl
as we no longer need this code. So what's left is:
FilenameGenerator.java
ReportIncidentRoutes.java
And that is all what's needed, well for now...
The idea now goal is to replace the endpoint the starts the ball going from previous staring endpoint ("direct:start") to the new starting CXF endpoint.
CXF endpoint can be configured in either or both CXF spring configuration file or/and in the route directly. It accepts one parameter and others are optional. The parameter it must have is the service class. The service class is the interface for the WSDL operation's. As we have the wsdl2java goal to generate this class for us, we have it already as org.apache.camel.example.reportincident.ReportIncidentEndpoint
.
...
Code Block | ||||
---|---|---|---|---|
| ||||
// return OK as response .transform(constant(OK)); |
Important issue regarding using CXF endpoints in Camel
Now we are nearly there, there is an important issue left with using CXF endpoints in Camel. In part 4 we started the route by sending the InputReportIncident object containing the webservice input. Now we are using CXF endpoints directly in our routing so its a CxfExchange that is created and passed in the routing. CxfExchange stores the payload in a CXF holder class org.apache.cxf.message.MessageContentsList
. So to be able to get our InputReportIncident class we need to get this object from the holder class. For this we show how it's done in Java using a processor, then later we show a nicer solution.
...
Now the route is nice and simple.
Unit testing
...
Now lets turn our attention to unit testing it. From part 4 we have an unit test that is capable of exposing a webservice and send a test request and assert a mail is received. We will refactor this unit test to start up Camel, as it's Camel that should expose the webservice.
...
Code Block | ||||
---|---|---|---|---|
| ||||
/** * Unit test of our routes */ public class ReportIncidentRoutesTest extends TestCase { private CamelContext camel; // should be the same address as we have in our route private static String ADDRESS = "http://localhost:8080/part-five/webservices/incident"; protected void startCamel() throws Exception { camel = new DefaultCamelContext(); camel.addRoutes(new ReportIncidentRoutes()); camel.start(); } protected static ReportIncidentEndpoint createCXFClient() { // we use CXF to create a client for us as its easier than JAXWS and works JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean(); factory.setServiceClass(ReportIncidentEndpoint.class); factory.setAddress(ADDRESS); return (ReportIncidentEndpoint) factory.create(); } public void testRendportIncident() throws Exception { // start camel startCamel(); // assert mailbox is empty before starting Mailbox inbox = Mailbox.get("incident@mycompany.com"); assertEquals("Should not have mails", 0, inbox.size()); // create input parameter InputReportIncident input = new InputReportIncident(); input.setIncidentId("123"); input.setIncidentDate("2008-08-18"); input.setGivenName("Claus"); input.setFamilyName("Ibsen"); input.setSummary("Bla"); input.setDetails("Bla bla"); input.setEmail("davsclaus@apache.org"); input.setPhone("0045 2962 7576"); // create the webservice client and send the request ReportIncidentEndpoint client = createCXFClient(); OutputReportIncident out = client.reportIncident(input); // assert we got a OK back assertEquals("0", out.getCode()); // let some time pass to allow Camel to pickup the file and send it as an email Thread.sleep(3000); // assert mail box assertEquals("Should have got 1 mail", 1, inbox.size()); // stop camel camel.stop(); } } |
Conclusion
We have now seen how we have created a more much nicer solution leveraging Camel's powerful routing capabilities.
What we have here is routing logic with the help of the code comments could be understood by non developers. This is very powerful. In a later part we will look at some of the tools that Camel provides, for instance a tool to generate a nice diagrams of your routes
Code Block | ||||
---|---|---|---|---|
| ||||
public void configure() throws Exception {
// webservice response for OK
OutputReportIncident OK = new OutputReportIncident();
OK.setCode("0");
// endpoint to our CXF webservice
String cxfEndpoint = "cxf://http://localhost:8080/part-five/webservices/incident"
+ "?serviceClass=org.apache.camel.example.reportincident.ReportIncidentEndpoint"
+ "&wsdlURL=report_incident.wsdl";
// first part from the webservice -> file backup
from(cxfEndpoint)
// we need to convert the CXF payload to InputReportIncident that FilenameGenerator and velocity expects
.convertBodyTo(InputReportIncident.class)
// then set the file name using the FilenameGenerator bean
.setHeader(FileComponent.HEADER_FILE_NAME, BeanLanguage.bean(FilenameGenerator.class, "generateFilename"))
// and create the mail body using velocity templating
.to("velocity:MailBody.vm")
// and store the file
.to("file://target/subfolder")
// return OK as response
.transform(constant(OK));
// second part from the file backup -> send email
from("file://target/subfolder")
// set the subject of the email
.setHeader("subject", constant("new incident reported"))
// send the email
.to("smtp://someone@localhost?password=secret&to=incident@mycompany.com");
}
|
In the next part's look at using XML to create the route instead of Java code. Then it might be even more readable by non developers.