Using JAX-WS and JAXB with WebLogic Server 10

by Mike Wooten

09/26/2007

Compiling the JAX-WS Service Endpoint

The BEA jwsc Ant task is used to compile the JAX-WS service endpoint and produce the WAR file that will be deployed.

<target name="run-jwsc">


  <taskdef name="jwsc" classname="weblogic.wsee.tools.anttasks.JwscTask"

           classpathref="compile.classpath" />



  <jwsc

    destdir="${domain.home}/deployments/${project.name}"

    srcdir="${src.dir}/${package.path}"

    classpath="WebContent/WEB-INF/lib/${wsdl.file.name}_wsdl.jar"

    keepGenerated="${keep}"

  >

     
                        
<binding dir="etc" includes="${client.binding}"/>

    <module explode="false" name="${portType.name}Impl">

      <jws

         
                        
type="JAXWS"

          file="${service.name}Impl.java"

          compiledWsdl="WebContent/WEB-INF/lib/${wsdl.file.name}_wsdl.jar"

      >

         
                        
<WLHttpTransport

            contextPath="datastaging"

          serviceUri="DataStagingService"

          portName="DataStagingServicePort"

        />

      </jws>

    </module>

    <classpath>

      <path refid="compile.classpath"/>

      <pathelement location="WebContent/WEB-INF/lib/${wsdl.file.name}_wsdl.jar"/>

    </classpath>

  </jwsc> 

</target>

Again, the main things to note are the type="JAXWS" attribute and the <binding> child element. It also illustrates a way to avoid putting WebLogic-specific annotations (for example, @WLHttpTransport) in the JWS.

Deploying with a build.xml File

One of the oft-touted features of JAX-WS is that it makes the use of deployment descriptors optional. This is nice, because it removes portability issues between JAX-WS implementations provided by multiple vendors.

POJO-based JAX-WS Web services are packaged as Java EE Web applications, and WebLogic Server 10 provides a wsdeploy Ant task for deploying them to a running WLS instance. I leverage this wsdeploy Ant task in the build.xml file I use here.

<target name="deploy">


  <property name="wls.username" value="weblogic"/>

  <property name="wls.password" value="weblogic"/>

  <property name="wls.hostname" value="localhost"/>

  <property name="wls.port" value="7031"/>

  <property name="wls.server.name" value="W4WPAdminServer"/>



  <taskdef name="wldeploy" 

           classname="weblogic.ant.taskdefs.management.WLDeploy"

           classpathref="compile.classpath" />

  

  <wldeploy

    action="deploy"

    name="${project.name}"

    source="${domain.home}/deployments/${project.name}"

    user="${wls.username}"

    password="${wls.password}"

    verbose="true"

    adminurl="t3://${wls.hostname}:${wls.port}" 

    targets="${wls.server.name}"

  />

</target>

Testing with the WebLogic Test Client

The WebLogic Test Client is a Java EE Web application, which, like the WebLogic Server Administration Console (console.war), is automatically deployed in your WebLogic Server 10.1 instance. The URL for it is:

http://<host>:<port>/wls_utc

The URL for the WSDL of the DataStagingService is:

http://<host>:<port>/datastaging/DataStagingService?WSDL

When you click the "Test" button after entering this WSDL URL, you'll be presented with the page that lets you enter a test request XML. Here's a sample request XML:

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


<dataStaging xmlns="http://services.irad.acmeworld.com/datastaging">

  <inputURIs>

    <inputURI>

      <uri>http://www.altova.com</uri>

    </inputURI>

    <inputURI>

      <uri>http://www.amazon.com</uri>

    </inputURI>

  </inputURIs>

</dataStaging>

The WebLogic Test Client is cool, but it doesn't really provide any insight into how to use JAXB and JAX-WS on the client side. The final walk-through step does that.

Testing with a JAX-WS Web Service Client

The final walk-through step looks at the JAX-WS Web service client used to invoke the DataStagingService Web service. This ended up being a bit more complex than the code for the JAX-WS service endpoint, when it came to using the JAXB API.

try


{

    DataStagingService service = new DataStagingService(

        new URL("http://localhost:7031/datastaging/DataStagingService?WSDL"),

        new QName("http://services.irad.acmeworld.com/datastaging",

                  _properties.getProperty("datastaging.service.portName"))

    );



    Dispatch<source> sourceDispatch = service.createDispatch(

        new QName("http://services.irad.acmeworld.com/datastaging",

                  "DataStagingService"),

        Source.class, 

        Service.Mode.PAYLOAD

    );



    InputStream inputstream = Thread.currentThread().getContextClassLoader().

                 getResourceAsStream("SampleDataStagingRequestDocument.xml");



    Source responseSource = sourceDispatch.invoke(new StreamSource(inputstream));



    javax.xml.transform.TransformerFactory factory =

                         javax.xml.transform.TransformerFactory.newInstance();



    javax.xml.transform.Transformer transformer = factory.newTransformer();

    transformer.setOutputProperty(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes");

    transformer.setOutputProperty(javax.xml.transform.OutputKeys.METHOD, "xml");



    javax.xml.transform.stream.StreamResult streamResult = 

                               new javax.xml.transform.stream.StreamResult();

 

    streamResult.setOutputStream(new java.io.ByteArrayOutputStream());

    transformer.transform(responseSource, streamResult);



    System.out.println((new StringBuffer()).append("response=").

                        append(streamResult.getOutputStream()).toString());

}

catch(Throwable throwable)

{

    throwable.printStackTrace();

    System.out.println((new StringBuffer()).append("Unexpected exception: ").

                        append(throwable.getMessage()).toString());

}

Most of the code is devoted to performing XSL transformations, using the JAXP API classes in the JDK. It starts out by creating a DataStagingService service stub. It accepts a URL object that points to the service endpoint's WSDL, and some XML-related for establishing the service being called. The latter is used after the WSDL is retrieved.

Note that the DataStagingService service stub and related classes were generated by the BEA clientgen Ant task. I don't show it here, but that Ant task has a type="JAXWS" attribute and <binding> child element as well.

The remaining code in the extract covers:

  • Creating the Dispatch object that is used to invoke the Web service operation. The Dispatch object is functionally equivalent to the Call object (used with the DII programming model) in JAX-RPC.
  • Creating the payload from an XML document. This is a welcome departure from the "instantiate an object for each element" exercise, required for JAX-RPC.
  • Invoking the Web service operation. You'll need to be a little careful here, because there is no auto-magic adding of an "operation name" wrapper element. If the service endpoint expects one, you'll need to add it yourself.
  • Using JAXP transformation classes to marshal the response from the Web service operation into a java.io.ByteArrayOutputStream. From there, you can go wherever you need to go (for example, byte[], String, XML). Again, this is much less onerous than dealing with graphs of JavaBeans (or worse, javax.xml.soap.SOAPElement objects), like you have to do with JAX-RPC.

This concludes the walk through. Hopefully it has given you a better understanding of how to use the JAX-WS 2.0 and JAXB 2.0 implementation in WebLogic Server 10. You've been able to see that these implementations have APIs, which are used on both the service-provider and service-consumer side. When you look at the sample code I've provided, you'll see that none of it uses (or imports) any WebLogic-specific classes. This means that the code is completely portable and should compile on Axis2, without having to make a single code (or customization file) change. You will, however, need to change the build.xml file, to use the .jar files that Axis2 uses for its JAX-WS and JAXB implementation. You'll also need to change Ant tasks used for JAX-WS, JAXB, and deployment.

A Few Best Practices

Here's a short list of best practices I've culled from using the JAX-WS and JAXB implementation in WebLogic Server 10:

  • Avoid using vendor-specific annotations in code where possible. Doing this avoids having to make code changes just to try out JAX-WS implementations from different vendors. JAX-WS implementation vendors typically provide ways to access vendor-specific annotations in their Ant tasks. WebLogic Server uses that option, so you should leverage it instead of using their vendor-specific annotations in your JWSs.
  • Use JAXP StreamSource and StreamResult, where possible. The javax.xml.transform.stream.StreamSource and javax.xml.transform.stream.StreamResult classes offer the most efficient way to deal with Java serialization (and deserialization) issues. This being the case, you should use them whenever and wherever possible.
  • Cache JAXBContext objects. The javax.xml.bind.JAXBContext object is a factory for creating JAXB objects from a Java class. It is a relatively expensive operation to create a JAXBContext object, so you should cache them for reuse.
  • Use JAXBElement to marshal inner classes. JAXB 2.0 uses inner classes for anonymous complexType elements. If you then use one of these in the method signature of a Web service operation, you will receive a javax.xml.bind.MarshalException during the build. The problem is that the inner class(es) is not a top-level element, so it doesn't have a @XmlRootElement annotation. The fix is to create a JAXBElement object for the inner class. Just do a search on " JAXBElement" in the DataStagingServiceImpl.java file to get a feel for how to do this.

Conclusion

JAX-WS and JAXB are two of the most promising APIs for constructing next-generation, Java-based Web services. The WebLogic Server 10 Web Services stack offers support for both of these APIs today, via Glassfish JARs and BEA Ant tasks.

You can use the WebLogic Server 10 Web Services stack to write, build, and deploy JAX-WS Web services for any JAX-WS implementation, not just the one in the WebLogic Server 10. The jwsc and wsdlc Ant tasks have been modified to allow you to specify that a customization file be used when generating the JAX-WS and JAXB artifacts. This is accomplished using the <binding> child element. The jwsc and wsdlc Ant tasks call Sun's wsimport Ant task, internally.

Hopefully, this article has given you a glimpse of how easy it is to instruct the WebLogic Server 10 Web Services stack, to generate JAX-WS and JAXB artifacts. What's not so easy is creating the customization files, which allow you to affect the artifacts' generation process. But don't worry. I've got several upcoming articles that are completely devoted to the topic of creating customization files.

References

The following links provide additional information on things discussed in this article:

Mike Wooten is a Sr. Principal Solutions Engineer in BEA's prestigious TSG (Technical Solutions Group). His waking hours are usually spent helping BEA customers turn their abstract technical notions/concepts, into revenue-generating solutions that exceed the capabilties offered by their competitors.