...
Info | ||
---|---|---|
| ||
Work in progress by Claus Ibsen. Tutorial to be part of Camel 1.5. (CAMEL-601) |
TODO: prerequisites
TODO: motivation for web application
TODO: State its part 1 of a series
Introduction
Creating this tutorial was inspired by a real life use-case I discussed over the phone with a college. He was working a client that uses a heavy-weight integration platform from very large vendor. He was in talks with contractors to implement a new integration on this heavy piece of platform - his trouble was though that the price was tripled when the contractors heard it had to be on this platform. So I was wondering how we could do this integration on Camel. Can it be done in one day.
...
Code Block | ||||
---|---|---|---|---|
| ||||
<?xml version="1.0" encoding="ISO-8859-1"?> <wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://reportincident.example.camel.apache.org" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://reportincident.example.camel.apache.org"> <!-- Type definitions for input- and output parameters for webservice --> <wsdl:types> <xs:schema targetNamespace="http://reportincident.example.camel.apache.org"> <xs:element name="inputReportIncident"> <xs:complexType> <xs:sequence> <xs:element type="xs:string" name="incidentId"/> <xs:element type="xs:string" name="incidentDate"/> <xs:element type="xs:string" name="givenName"/> <xs:element type="xs:string" name="familyName"/> <xs:element type="xs:string" name="summary"/> <xs:element type="xs:string" name="details"/> <xs:element type="xs:string" name="email"/> <xs:element type="xs:string" name="phone"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="outputReportIncident"> <xs:complexType> <xs:sequence> <xs:element type="xs:string" name="code"/> </xs:sequence> </xs:complexType> </xs:element> </xs:schema> </wsdl:types> <!-- Message definitions for input and output --> <wsdl:message name="inputReportIncident"> <wsdl:part name="parameters" element="tns:inputReportIncident"/> </wsdl:message> <wsdl:message name="outputReportIncident"> <wsdl:part name="parameters" element="tns:outputReportIncident"/> </wsdl:message> <!-- Port (interface) definitions --> <wsdl:portType name="ReportIncidentServiceReportIncidentEndpoint"> <wsdl:operation name="ReportIncident"> <wsdl:input message="tns:inputReportIncident"/> <wsdl:output message="tns:outputReportIncident"/> </wsdl:operation> </wsdl:portType> <!-- Port bindings to transports and encoding - HTTP, document literal encoding is used --> <wsdl:binding name="ReportIncidentBinding" type="tns:ReportIncidentServiceReportIncidentEndpoint"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="ReportIncident"> <soap:operation soapAction="http://reportincident.example.camel.apache.org/ReportIncident" style="document"/> <wsdl:input> <soap:body parts="parameters" use="literal"/> </wsdl:input> <wsdl:output> <soap:body parts="parameters" use="literal"/> </wsdl:output> </wsdl:operation> </wsdl:binding> <!-- Service definition --> <wsdl:service name="ReportIncidentService"> <wsdl:port name="ReportIncidentPort" binding="tns:ReportIncidentBinding"> <soap:address location="http://reportincident.example.camel.apache.org"/> </wsdl:port> </wsdl:service> </wsdl:definitions> |
...
Code Block | ||||
---|---|---|---|---|
| ||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <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-servlet.xml"/> <!-- implementation of the webservice --> <bean id="reportIncidentEndpoint" class="org.apache.camel.example.reportincident.ReportIncidentEndpointReportIncidentEndpointImpl"/> <!-- export the webservice using jaxws --> <jaxws:endpoint id="reportIncident" implementor="#reportIncidentEndpoint" address="/incident" wsdlLocation="/WEB-INF/wsdl/report_incident.xml" endpointName="s:ReportIncidentPort" serviceName="s:ReportIncidentService" xmlns:s="http://reportincident.example.camel.apache.org"/> </beans> |
...
As we want to quickly see our webservice we implement just a quick and dirty as it can get. At first beware that since its jaxws and Java 1.5 we get annotations for the money, but they reside on the interface so we can remove them from our implementations so its a nice plain POJO again:
Code Block | ||||
---|---|---|---|---|
| ||||
package org.apache.camel.example.reportincident; import javax.jws.soap.SOAPBinding; import javax.jws.WebResult; import javax.jws.WebMethod; import javax.jws.WebParam; /** * The webservice we have implemented. */ public class ReportIncidentEndpoint implements ReportIncidentServiceImpl /** * The webservice we have implemented. */ public class ReportIncidentEndpointImpl implements ReportIncidentEndpoint { public OutputReportIncident reportIncident(InputReportIncident parameters) { @SOAPBinding(parameterStyle = SOAPBindingSystem.ParameterStyle.BARE) @WebResult(name = "outputReportIncident",out.println("Hello ReportIncidentEndpointImpl is called from " + parameters.getGivenName()); targetNamespaceOutputReportIncident out = "http://reportincident.example.camel.apache.org", partName = "parameters") new OutputReportIncident(); @WebMethod(operationName = out.setCode("ReportIncidentOK",); return actionout; = "http://reportincident.example.camel.apache.org/ReportIncident") public OutputReportIncident reportIncident(@WebParam(partName = "parameters", } } |
We just output the person that invokes this webservice and returns a OK response. This class should be in the maven source root folder src/main/java
under the package name org.apache.camel.example.reportincident
. Beware that the maven archetype tool didn't create the src/main/java folder
, so you should create it manually.
To test if we are home free we run mvn clean compile
.
Running our webservice
Now that the code compiles we would like to run it in a web container, so we add jetty to our pom.xml so we can run mvn retty:run
:
Code Block | ||||
---|---|---|---|---|
| ||||
<properties> name = "inputReportIncident", targetNamespace = "http://reportincident.example.camel.apache.org") InputReportIncident parameters) { System.out.println("Hello this webservice is called by " + parameters.getGivenName()); <jetty-version>6.1.1</jetty-version> </properties> <build> OutputReportIncident out =<plugins> new OutputReportIncident(); out.setCode("OK");... <!-- so we can run mvn return out; jetty:run --> } } |
We just output the person that invokes this webservice and returns a OK response. This class should be in the maven source root folder src/main/java
under the package name org.apache.camel.example.reportincident
. Beware that the maven archetype tool didn't create the src/main/java folder
, so you should create it manually.
To test if we are home free we run mvn clean compile
.
Running our webservice
Now that the code compiles we would like to run it in a web container, so we add jetty to our pom.xml so we can run mvn retty:run
:
...
<properties>
...
<jetty-version>6.1.1</jetty-version>
</properties>
<build>
<plugins>
...
<!-- so we can run mvn jetty:run -->
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>${jetty-version}</version>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>${jetty-version}</version>
</plugin>
|
Notice: We use Jetty v6.1.1 as never versions has troubles on my laptop. Feel free to try a newer version on your system, but v6.1.1 works flawless.
So to see if everything is in order we fire up jetty with mvn jetty:run
and if everything is okay you should be able to access http://localhost:8080
.
Jetty is smart that it will list the correct URI on the page to our web application, so just click on the link. This is smart as you don't have to remember the exact web context URI for your application - just fire up the default page and Jetty will help you.
So where is the damm webservice then? Well as we did configure the web.xml to instruct the CXF servlet to accept the pattern /webservices/*
we should hit this URL to get the attention of CXF: http://localhost:8080/my-webapp/webservices
.
Hitting the webservice
Now we have the webservice running in a standard .war application in a standard web container such as Jetty we would like to invoke the webservice and see if we get our code executed. Unfortunately this isn't the easiest task in the world - its not so easy as a REST URL, so we need tools for this. So we fire up our trusty webservice tool SoapUI and let it be the one to fire the webservice request and see the response.
Using SoapUI we sent a request to our webservice and we got the expected OK response and the console outputs the System.out so we are ready to code.
Remote Debugging
Okay a little sidestep but wouldn't it be cool to be able to debug your code when its fired up under Jetty? As Jetty is started from maven, we need to instruct maven to use debug mode.
Se we set the MAVEN_OPTS
environment to start in debug mode and listen on port 5005.
Code Block |
---|
MAVEN_OPTS=-Xmx512m -XX:MaxPermSize=128m -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005
|
Then you need to restart Jetty so its stopped with ctrl + c. Remember to start a new shell to pickup the new environment settings. And start jetty again.
Then we can from our IDE attach a remote debugger and debug as we want.
First we configure IDEA to attach to a remote debugger on port 5005:
Then we set a breakpoint in our code ReportIncidentEndpoint
and hit the SoapUI once again and we are breaked at the breakpoint where we can inspect the parameters:
Adding a unit test
Oh so much hard work just to hit a webservice, why can't we just use an unit test to invoke our webservice? Yes of course we can do this, and that's the next step.
First we create the folder structure src/test/java
and src/test/resources
. We then create the unit test in the src/test/java
folder.
Code Block | ||||
---|---|---|---|---|
| ||||
package org.apache.camel.example.reportincident;
import junit.framework.TestCase;
/**
* Plain JUnit test of our webservice.
*/
public class ReportIncidentEndpointTest extends TestCase {
}
|
Here we have a plain old JUnit class. As we want to test webservices we need to start and expose our webservice in the unit test before we can test it. And JAXWS has pretty decent methods to help us here, the code is simple as:
Code Block | ||||
---|---|---|---|---|
| ||||
import javax.xml.ws.Endpoint;
...
private static String ADDRESS = "http://localhost:9090/unittest";
protected void startServer() throws Exception {
// We need to start a server that exposes or webservice during the unit testing
// We use jaxws to do this pretty simple
ReportIncidentEndpointImpl server = new ReportIncidentEndpointImpl();
Endpoint.publish(ADDRESS, server);
}
|
The Endpoint class is the javax.xml.ws.Endpoint
that under the covers looks for a provider and in our case its CXF - so its CXF that does the heavy lifting of exposing out webservice on the given URL address. Since our class ReportIncidentEndpointImpl implements the interface ReportIncidentEndpoint that is decorated with all the jaxws annotations it got all the information it need to expose the webservice. Below is the CXF wsdl2java generated interface:
Code Block | ||||
---|---|---|---|---|
| ||||
/*
*
*/
package org.apache.camel.example.reportincident;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.ParameterStyle;
import javax.xml.bind.annotation.XmlSeeAlso;
/**
* This class was generated by Apache CXF 2.1.1
* Wed Jul 16 12:40:31 CEST 2008
* Generated source version: 2.1.1
*
*/
/*
*
*/
@WebService(targetNamespace = "http://reportincident.example.camel.apache.org", name = "ReportIncidentEndpoint")
@XmlSeeAlso({ObjectFactory.class})
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface ReportIncidentEndpoint {
/*
*
*/
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
@WebResult(name = "outputReportIncident", targetNamespace = "http://reportincident.example.camel.apache.org", partName = "parameters")
@WebMethod(operationName = "ReportIncident", action = "http://reportincident.example.camel.apache.org/ReportIncident")
public OutputReportIncident reportIncident(
@WebParam(partName = "parameters", name = "inputReportIncident", targetNamespace = "http://reportincident.example.camel.apache.org")
InputReportIncident parameters
);
}
|
Next up is to create a webservice client so we can invoke our webservice. For this we actually use the CXF framework directly as its a bit more easier to create a client using this framework than using the JAXWS style. We could have done the same for the server part, and you should do this if you need more power and access more advanced features.
Code Block | ||||
---|---|---|---|---|
| ||||
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
...
protected 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();
}
|
So now we are ready for creating a unit test. We have the server and the client. So we just create a plain simple unit test method as the usual junit style:
Code Block | ||||
---|---|---|---|---|
| ||||
public void testRendportIncident() throws Exception {
startServer();
ReportIncidentEndpoint client = createCXFClient();
InputReportIncident input = new InputReportIncident();
input.setIncidentId("123");
input.setIncidentDate("2008-07-16");
input.setGivenName("Claus");
input.setFamilyName("Ibsen");
input.setSummary("bla bla");
input.setDetails("more bla bla");
input.setEmail("davsclaus@apache.org");
input.setPhone("+45 2962 7576");
OutputReportIncident out = client.reportIncident(input);
assertEquals("Response code is wrong", "OK", out.getCode());
}
|
Now we are nearly there. But if you run the unit test with mvn test
then it will fail. Why!!! Well its because that CXF needs is missing some dependencies during unit testing. In fact it needs the web container, so we need to add this to our pom.xml.
Code Block | ||||
---|---|---|---|---|
| ||||
<!-- cxf web container for unit testing -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>${cxf-version}</version>
<scope>test</scope>
</dependency>
|
Well what is that, CXF also uses Jetty for unit test - well its just shows how agile, embedable and popular Jetty is.
So lets run our junit test with, and it reports:
Code Block |
---|
mvn test
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESSFUL
|
Yep thats it for now. We have a basic project setup.
End of part 1
TODO: Conclusion
TODO: Teaser for part 2
Notice: We use Jetty v6.1.1 as never versions has troubles on my laptop. Feel free to try a newer version on your system, but v6.1.1 works flawless.
So to see if everything is in order we fire up jetty with mvn jetty:run
and if everything is okay you should be able to access http://localhost:8080
.
Jetty is smart that it will list the correct URI on the page to our web application, so just click on the link. This is smart as you don't have to remember the exact web context URI for your application - just fire up the default page and Jetty will help you.
So where is the damm webservice then? Well as we did configure the web.xml to instruct the CXF servlet to accept the pattern /webservices/*
we should hit this URL to get the attention of CXF: http://localhost:8080/my-webapp/webservices
.
Hitting the webservice
Now we have the webservice running in a standard .war application in a standard web container such as Jetty we would like to invoke the webservice and see if we get our code executed. Unfortunately this isn't the easiest task in the world - its not so easy as a REST URL, so we need tools for this. So we fire up our trusty webservice tool SoapUI and let it be the one to fire the webservice request and see the response.
Using SoapUI we sent a request to our webservice and we got the expected OK response and the console outputs the System.out so we are ready to code.
Remote Debugging
Okay a little sidestep but wouldn't it be cool to be able to debug your code when its fired up under Jetty? As Jetty is started from maven, we need to instruct maven to use debug mode.
Se we set the MAVEN_OPTS
environment to start in debug mode and listen on port 5005.
Code Block |
---|
MAVEN_OPTS=-Xmx512m -XX:MaxPermSize=128m -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005
|
Then you need to restart Jetty so its stopped with ctrl + c. Remember to start a new shell to pickup the new environment settings. And start jetty again.
Then we can from our IDE attach a remote debugger and debug as we want.
First we configure IDEA to attach to a remote debugger on port 5005:
Then we set a breakpoint in our code ReportIncidentEndpoint
and hit the SoapUI once again and we are breaked at the breakpoint where we can inspect the parameters:
Adding a unit test
Oh so much hard work just to hit a webservice, why can't we just use an unit test to invoke our webservice? Yes of course we can do this, and that's the next step.
TODO: CXF unit test
End of part 1
TODO: Conclusion.
Then