1
votes

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

2

2 Answers

2
votes

Do you have a real war or do you have a jar using the http service, cause right now the way the blueprint xml looks like you define a servlet within the blueprint xml. Though you talk about a war that contains servlets and jsps. Beware you have two different extenders taking care of the servlet and the blueprint context, both can't be mixed. So you need to make sure you have a way of accessing the bundle context from the servlet.

Take a look at either the whiteboard-blueprint sample or the war-spring sample. The first one only uses blueprint, the other one mixes a war with spring-dm, which will also work with spring 3.

1
votes

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