You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Next »

Part 2 (con't)

Webservice

This part has already been discussed in detail in the excellent tutorial : Report Incident - This tutorial introduces Camel steadily and is based on a real life integration problem. So we will only explain what we have done specifically for our project.

Compare to the other tutorial, we have packaged the code generated by the CXF framework in a project/bundle separated from the routing/mediation engine. This approach allows you to extend your web services (I mean the methods exposed) without impacting the rest of your application. The question concerning the model is mush more delicate because we have a dependency on the model created to persist information. In our case, we have separated the webservice model (where the fields available are all declared as string) from ours but you can considered to have the same when Types are compatible (e.g. Can I map the Date Time object of my webservice field to my model without any transformation ?).

To generate the code that our application will use, we will work with following WSDL contract report_incident.wsdl that you create in the directory src/main/resources/META-INF/wsdl:

<?xml version="1.0" encoding="UTF-8"?>

<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 name="inputReportIncident">
                    <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 name="outputReportIncident">
                    <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="in" element="tns:inputReportIncident"/>
    </wsdl:message>
    <wsdl:message name="outputReportIncident">
        <wsdl:part name="out" element="tns:outputReportIncident"/>
    </wsdl:message>

    <!-- Port (interface) definitions -->
    <wsdl:portType name="ReportIncidentEndpoint">
        <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:ReportIncidentEndpoint">
        <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="in" use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body parts="out" use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>

    <!-- Service definition -->
    <wsdl:service name="ReportIncidentEndpointService">
        <wsdl:port name="ReportIncidentPort" binding="tns:ReportIncidentBinding">
            <soap:address location="http://localhost:8080/camel-example/incident"/>
        </wsdl:port>
    </wsdl:service>

</wsdl:definitions>

The code will be generated thanks to a maven plugin : cxf-codegen-plugin.

Add the following line in your pom.xml of the project reportincident.webservice

			<!-- CXF wsdl2java generator, will plugin to the compile goal -->
			<plugin>
				<groupId>org.apache.cxf</groupId>
				<artifactId>cxf-codegen-plugin</artifactId>
				<version>${cxf-version}</version>
				<executions>
					<execution>
						<id>generate-sources</id>
						<phase>generate-sources</phase>
						<configuration>
							<sourceRoot>${basedir}/target/generated/src/main/java</sourceRoot>
							<wsdlOptions>
								<wsdlOption>
									<wsdl>${basedir}/src/main/resources/META-INF/wsdl/report_incident.wsdl</wsdl>
								</wsdlOption>
							</wsdlOptions>
						</configuration>
						<goals>
							<goal>wsdl2java</goal>
						</goals>
					</execution>
				</executions>

			</plugin>

The code is generated using the maven command :

mvn generate-sources

Remark : the code is generated in the directory target/src/main/java

Everything is in place to integrate the services together except the routing and OSGI stuffs. This is what we will cover in the following sections.

Transform existing projects into bundles

To convert a project into a bundle that you will be able to deploy into an OSGI server, a couple of things/steps must be done :

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) Register services when they are used by another bundle.

Remark : 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.

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 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>

	<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>

Remarks :

  • The model bundle does not need to import 'specific' packages so the tag <Import-Packages> can be skipped but packages will be added automatically
    by the felix/bnd tool to the MANIFEST.MF file if import statements are defined in the classes or maven dependencies are declared
  • The tag <_failok>true</_failok> is added to inform the plugin that it can continue the creation of the MANIFEST.MF file even if an error/warning occurs. This is the case here because files located under META-INF directory must be exported.
  • The hibernate file must be exported with the classpath of the bundle. This is why the line '=META-INF.org.apache.camel.example.reportincident.model' has
    been added to export the packagename containing this file

When the pom.xml has been updated, you can execute the following maven command :

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

Remark : 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

Manifest-Version: 1.0
Export-Package: org.apache.camel.example.reportincident.model;uses:="o
 rg.apache.commons.lang.builder,org.apache.camel.dataformat.bindy.anno
 tation",META-INF.org.apache.camel.example.reportincident.model
Built-By: Charlesm
Build-Jdk: 1.6.0_12
Bundle-Version: 1.0.0.SNAPSHOT
Tool: Bnd-0.0.255
Bundle-Name: Report Incident Model Bundle
Bnd-LastModified: 1240844069542
Created-By: Apache Maven Bundle Plugin
Bundle-ManifestVersion: 2
Bundle-SymbolicName: reportincident.model
Import-Package: META-INF.org.apache.camel.example.reportincident.model
 ,org.apache.camel.dataformat.bindy.annotation;version="2.0.0.M1",org.
 apache.camel.example.reportincident.model,org.apache.commons.lang.bui
 lder;version="2.4"

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

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 }} and defined {{ as private. Why, the reason is very simple. We would like to export only the interface to another 'service' bundles and keep internally the implementation.

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

Discovering all the classes used by a third party library like Hibernate can be cumbersome and takes time. An interesting alternative is to use the command 'DynamicImport-Package' to resolve classloading issue. <DynamicImport-Package> *</DynamicImport-Package>

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

    <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>

and look at the result generated by the plugin. The Import-Package section is dry.

Now that our pom.xml is configurated we will modified our spring.xml files to allow our DAO service to be registered as a OSGI service.

  • Create the file persistence-osgi.xml in the dirfectory 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: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>

Routing/Mediation service

The routing/mediation between services/components will be created using Spring DSL language.

  • No labels