Search code examples
web-servicesosgicxfapache-karafblueprint-osgi

How to build a web service client with blueprint


we have built a WAR with one servlet and one JPS and converted it to make a bundle by using the maven bundle plugin. The servlet and jsp's are running fine in Apache Karaf with pax-web. Now I would like to use a web service client in that servlet. How can I achieve that?

So far we used the cxf-codegen maven plugin to create all required classes to build a client. We have all the dependencies: cxf-rt-transports-http, cxf-rt-ws-addr, cxf-rt-ws-policy, cxf-rt-frontend-jaxrs, cxf-rt-ws-security and cxf-rt-transports-http-jetty declared in maven. Futhermore I have the following entry inside the blueprint.xml:

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:jaxws="http://cxf.apache.org/blueprint/jaxws"
    xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 
   http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
   http://cxf.apache.org/blueprint/jaxws http://cxf.apache.org/schemas/blueprint/jaxws.xsd">


    <bean id="myServlet" class="com.production.dashboard.DataCombination">
            <property name="dataMergingService" ref="dataMergingService"/>
    </bean>

    <service ref="myServlet" interface="javax.servlet.http.HttpServlet">
            <service-properties>
                    <entry key="alias" value="/hello" />
            </service-properties>
    </service>

    <jaxws:client id="dataMergingService"
            serviceClass="com.production.engine.datacombination.OrderDataMergingService"
            address="http://localhost:8181/engine/datacombination?wsdl" />

When I use this approach the injection fails because the client is always null.

Could anybody please explain me how a web service client has to be used in OSGi, blueprint and in conjunction with a war enabled bundle?

Many thanks in advance.

Cheers Hilderich


Solution

  • It's nice to hear from you. In the meantime I have reached a better understanding about Java Web Applications running in OSGi containers.

    First of all my application is an ordinary Java Web Application, i.e. a WAR. With the additional OSGi Manifest meta data (Web-ContextPath, Webapp-Context) that WAR was enabled to be a Web Application Bundle (WAB). Furthermore as you mentioned above the blueprint.xml wasn't recognized by the OSGi container Apache Karaf because there was no Manifest meta data (Bundle-Blueprint) the Blueprint Extender is trying to detect.

    The maven bundle plugin (i.e. the bnd tool) is building the WAB on every build.

    <plugin>
           <groupId>org.apache.felix</groupId>
            <artifactId>maven-bundle-plugin</artifactId>
            <inherited>true</inherited>
            <executions>
               <execution>
                <id>bundle-manifest</id>
                <phase>process-classes</phase>
                <goals>
                      <goal>manifest</goal>
                </goals>
                <configuration>
                         <supportedProjectTypes>
                    <supportedProjectType>jar</supportedProjectType>
                    <supportedProjectType>bundle</supportedProjectType>
                    <supportedProjectType>war</supportedProjectType>
                      </supportedProjectTypes>
                <instructions>
                    <Bundle-SymbolicName>${web.contextPath}</Bundle-SymbolicName>
                    <Bundle-ClassPath>
                        .,
                        WEB-INF/classes,
                        WEB-INF/lib/jstl-1-2.jar,
                        WEB-INF/lib/ops4j-base-io-1.4.0.jar,
                        WEB-INF/lib/ops4j-base-lang-1.4.0.jar,
                        WEB-INF/lib/ops4j-base-monitors-1.4.0.jar,
                        WEB-INF/lib/ops4j-base-store-1.4.0.jar,
                        WEB-INF/lib/ops4j-base-util-property-1.4.0.jar,
                        WEB-INF/lib/org.ops4j.pax.tipi.hamcrest.core-1.3.0.1.jar,
                        WEB-INF/lib/standard-1.1.2.jar
                    </Bundle-ClassPath>
                    <Bundle-Blueprint>WEB-INF/classes/OSGI-INF/blueprint/*.xml</Bundle-Blueprint>
                    <Web-ContextPath>${web.contextPath}</Web-ContextPath>
                        <Webapp-Context>${web.contextPath}</Webapp-Context>
                    <Export-Package>
                        !org.production.engine.datacombination
                    </Export-Package>
                    <Import-Package>
                        javax.servlet,
                        javax.servlet.http,
                        javax.servlet.*,
                        javax.servlet.jsp.*,
                        javax.servlet.jsp.jstl.*,
                        !junit.framework,
                        !org.junit,
                        !sun.misc,
                            !org.ops4j.pax.swissbox.*,
                            *
                    </Import-Package>
                    <DynamicImport-Package>
                        javax.*,
                        org.xml.sax,
                        org.xml.sax.*,
                        org.w3c.*
                    </DynamicImport-Package>
                </instructions>
                </configuration>
            </execution>
        </executions>
    
    </plugin>
    

    However to run the web application in Apache Karaf you must install the feature war:

    features:install war
    

    In addition the jre 1.6 had to export further packages (excerpt of jre.properties):

    jre-1.6= \
         ...
         com.sun.org.apache.xalan.internal.res, \
         com.sun.org.apache.xml.internal.utils, \
         com.sun.org.apache.xpath.internal, \
         com.sun.org.apache.xpath.internal.jaxp, \
         com.sun.org.apache.xpath.internal.objects
    

    With all that the Servlet Container (Jetty) was running and JSP pages were rendered correctly.

    Now I explain how to use the Web Service Client within the Servlet. With the WSDL file located in the resource directory I create all essential classes in order to build the Web Service Client. To do that easily the Maven cxf-codegen-plugin creates these classes:

    <plugin>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-codegen-plugin</artifactId>
        <executions>
            <execution>
                <id>generate-sources</id>
                <phase>generate-sources</phase>
                <configuration>
                    <sourceRoot>${project.build.directory}/generated/cxf</sourceRoot>
                    <encoding>UTF-8</encoding>
                    <wsdlOptions>
                        <wsdlOption>
                            <wsdl>src/main/resources/datacombination_1.wsdl</wsdl>
                            <wsdlLocation>classpath:datacombination_1.wsdl</wsdlLocation>
                            <extraargs>
                                <extraarg>-b</extraarg>
                                <extraarg>${basedir}/src/main/resources/jaxb-binding-date.xml</extraarg>
                                <extraarg>-compile</extraarg>
                            </extraargs>
                        </wsdlOption>
                    </wsdlOptions>
                </configuration>
                <goals>
                    <goal>wsdl2java</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
    

    Now I can connect the generated web service classes with the real Web Service inside the blueprint.xml and publish it as an OSGi service:

    <jaxws:client id="dms"
            serviceClass="org.production.engine.datacombination.OrderDataMerging"
            address="/engine/datacombination" 
            wsdlLocation="classpath:/datacombination_1.wsdl"
            serviceName="ns1:OrderDataMergingService" 
            endpointName="ns1:OrderDataMergingPort" />  
    
    <service ref="dms" interface="org.production.engine.datacombination.OrderDataMerging" />
    

    Inside the Servlet class I was now able to instantiate the generated Service class and the Web Service was called on the remote machine:

    OrderDataMergingService dataMergingService = new OrderDataMergingService();
    String orderId = dataMergingService.getOrderDataMergingPort()
                    .importOrder(wsRequest);
    

    The one and only secret I haven't conceived yet why I have to publish that OSGi service? Because when the OSGi service (ref="dms" in blueprint.xml) is missing the Web Service Client doesn't work.

    Cheers Johannes