Showing posts with label JAX-WS. Show all posts
Showing posts with label JAX-WS. Show all posts

Thursday, January 15, 2015

JAX-WS or JAXB working with the any type

Let's say you are working with a Schema that uses an "any" type like in the ws-security schema: (http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd):



<xsd:complextype name="SecurityHeaderType">
 <xsd:annotation>
  <xsd:documentation>This complexType defines header block to use for security-relevant data directed at a specific SOAP actor.</xsd:documentation>
 </xsd:annotation>
 <xsd:sequence>
          <xsd:any maxoccurs="unbounded" minoccurs="0" processcontents="lax">
   <xsd:annotation>
    <xsd:documentation>The use of "any" is to allow extensibility and different forms of security data.</xsd:documentation>
   </xsd:annotation>
  </xsd:any>
 </xsd:sequence>
 <xsd:anyattribute namespace="##other" processcontents="lax">
</xsd:anyattribute></xsd:complextype>
You generate the Java code using xjc or wsimport and then are wondering how to use the generated SecurityHeaderType object to put something like a username and password inside of it.

You may try to do something like this:



                // build the security header
  SecurityHeaderType headerWSSE = new SecurityHeaderType();
  
  // Create the userName and password that will go in the security header
  UsernameTokenType userNameToken = new UsernameTokenType();
  AttributedString userNameAttribute = new AttributedString();
  userNameAttribute.setValue("joebob");
  userNameToken.setUsername(userNameAttribute );
  
  PasswordString passwordString = new PasswordString();
  passwordString.setValue("secret");
  userNameToken.getAny().add(passwordString);
  headerWSSE.getAny().add(userNameToken);

(NOTE: UsernameTokenType and PasswordString types are both defined in the ws-security schema found here.)
If you run with this code, you will most likely get an error like this when it attempts to create the XML:

javax.xml.ws.WebServiceException: com.sun.istack.internal.XMLStreamException2: javax.xml.bind.MarshalException
 - with linked exception:
[com.sun.istack.internal.SAXException2: unable to marshal type "org.oasis_open.docs.wss._2004._01.oasis_200401_wss_wssecurity_secext_1_0.UsernameTokenType" as an element because it is missing an @XmlRootElement annotation]

The reason is that when xjc or wsimport generated the JAXB annotated classes it didn't create XmlRootElement annotations for UsernameTokenType or PasswordString.  So, it doesn't know how to handle them properly when they are added to the "any" collection.

So, one fix, is to manually add the XmlRootElement annotations to the UsernameTokenType and PasswordString classes.  However, I generally do not like the idea of manually modifying generated code.  It should be throw away to a certain degree.  That is if the schema changes and I need to re-generate I'd like to be able to just wipe the previously generated code and re-generate the new code.

Another option, is to use a plug-in like the annotate plugin to allow you to create a custom JAXB binding file that will add the XmlRootElement annotation as part of the code generation process.
You can find the annotate plugin here. http://confluence.highsource.org/display/J2B/Annotate+Plugin

The third option you have is to write your code in such a way that it wraps the UsernameTokenType and PasswordString objects in JAXBElement objects that can tell it how to create the root element names.  Here's the code for that:



                // build the security header
  SecurityHeaderType headerWSSE = new SecurityHeaderType();
  
  // Create the userName and password that will go in the security header
  UsernameTokenType userNameToken = new UsernameTokenType();
  AttributedString userNameAttribute = new AttributedString();
  userNameAttribute.setValue("joebob");
  userNameToken.setUsername(userNameAttribute );
  
  PasswordString passwordString = new PasswordString();
  passwordString.setValue("secret");
  
  // Since SecurityHeaderType takes "any" type, we must create JAXBElements to properly set the root elements... stupid "any" type!
  QName qnamePassword = new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Password");
  JAXBElement jaxbPassword = new JAXBElement(qnamePassword ,PasswordString.class,passwordString);
  userNameToken.getAny().add(jaxbPassword);
  
  QName qname = new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "UsernameToken");
  JAXBElement jaxbUserName = new JAXBElement(qname ,UsernameTokenType.class,userNameToken);
   
  // Add our JAXBElements to the headers "any" list.
  headerWSSE.getAny().add(jaxbUserName);

This final approach requires you to create a QName instance to set the namespace and the root element name. Then you can create a JAXBElement that wraps your actual PasswordString and UsernameTokenType objects.  In my case, I wanted the PasswordString inside the UsernameToken so that's why you see that I added PasswordString into the usernameToken's any collection.

The code above produces the following XML:




<ns3:Security xmlns:ns3="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" >
 <ns3:UsernameToken>
  <ns3:Username>joebob</ns3:Username>
  <ns3:Password>secret</ns3:Password>
 </ns3:UsernameToken>
</ns3:Security>

Your namespace pre-fix may vary of course and I removed some of the extra namespace entries that were declared as part of the Security element since they weren't used in this node.

Hope this helps!  If you have any questions, feel free to post them below.

Thursday, September 18, 2014

JAX-WS overriding Parameter name when generating code


When generating JAX-WS from code, you may at times need to override the default parameter names of the generated code.  Actually, sometimes it's required if you are having parameter name conflicts.  That happened to me recently.  I had a wsdl that defined an input message and output message that both had a header part like this:

<wsdl:message name="Ping">
<wsdl:part name="headerWSSE" element="wsse:Security"/>
<wsdl:part name="headerSYS" element="tnv:SystemInfo_Request"/>
<wsdl:part name="ping" element="tnv:ping"/>
</wsdl:message>
<wsdl:message name="PingResponse">
<wsdl:part name="headerSYS" element="tnv:SystemInfo_Response"/>
<wsdl:part name="pingResponse" element="tnv:pingResponse"/>
</wsdl:message>

Notice that both the Ping and PingResponse message define a part with the name="headerSYS".

The issue here is that when the code generator tries to create the ping() method in Java, it tries to create a method with 5 parameters... 3 for the input message parts and 2 for the output message parts. However, it would try to use the part name "headerSYS" as a parameter name for one of the input and one of the ouput parameters and it would bomb out because of two parameters having the same name in the same method signature.

Therefore, one fairly simple solution is to just create a custom binding file that tells ws-import how to handle these parameter names.  Here's what the custom binding file would look like:




<jaxws:bindings wsdlLocation="TelephoneNumberVerification_1_0_Logical.wsdl"
                 xmlns:xs="http://www.w3.org/2001/XMLSchema"
                 xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
                 xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
                 xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
                 xmlns:tnv="http://barry.com/schemas/customerRelationship/Telephony">
       <jaxws:bindings node="wsdl:definitions/wsdl:portType[@name='TelephoneNumberVerification']/wsdl:operation[@name='ping']">
         <jaxws:parameter
                 part="wsdl:definitions/wsdl:message[@name='PingResponse']/wsdl:part[@name='headerSYS']"
                 childElementName="tnv:SystemInfo_Response" name="headerSysResponse"/>
     </jaxws:bindings>
     
 </jaxws:bindings>



I've attempted to hi-lite any thing that is custom for my wsdl that someone would have to change if they copied it and used it as a template.

By pointing ws-import to this custom binding, I was able to generate the code successfully and ended up with a method signature like this:



  public void ping(SecurityHeaderType headerWSSE, SystemInfoRequest headerSYS, boolean ping, Holder<SystemInfoResponse> headerSysResponse, Holder<Boolean> pingResponse)




Hope this helps!

Friday, August 8, 2014

Dozer Deep Nesting...A different approach. (No Setters, No Problem.)


Have you ever had the issue of trying to do a deep nested mapping using Dozer, but you keep running into issues because the destination object doesn't have a set method for the property you need to populate?

We see this a lot when dealing with generated JAX-WS code.  The JAXB classes generated from the schema typically do not create set methods for collections.  So, if you are trying to create a Dozer mapping to map from the JAX-WS/JAXB generated code to and from your application domain classes, you can run into some issues if you start trying to do deep mappings.

Here are the classes I want to populate via Dozer (made up for an example... all in package com.barry.to).  Starting with the first one and going down each class contains the class below it.  Usually in a List.



public class StartDestObject {
 
 private HaveNestedListNoSetter haveNestedListNoSetter;

 public HaveNestedListNoSetter getHaveNestedListNoSetter() {
  return haveNestedListNoSetter;
 }

 public void setHaveNestedListNoSetter(HaveNestedListNoSetter haveNestedListNoSetter) {
  this.haveNestedListNoSetter = haveNestedListNoSetter;
 }

}

public class HaveNestedListNoSetter {
 
 private List<HaveListNoSetter> haveListNoSetterList = new ArrayList<HaveListNoSetter>();

 public List<HaveListNoSetter> getHaveListNoSetterList() {
  return haveListNoSetterList;
 }
}

public class HaveListNoSetter {
 
 private List<Thing> things = new ArrayList<Thing>();

 public List<Thing> getThings() {
  return things;
 } 
}

public class Thing {

 private String name;
 private String value;
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public String getValue() {
  return value;
 }
 public void setValue(String value) {
  this.value = value;
 }
 
}


Notice that none of my classes that contain collections have any set methods for the List.

So, the idea is that I want to map a much simpler object into this object tree...  Here's my much simpler object structure:

package com.barry.from;

import java.util.List;

public class ItemHolder {
 
 private List<Item> items;

 public List<Item> getItems() {
  return items;
 }

 public void setItems(List<Item> items) {
  this.items = items;
 }
}
public class Item {
 
 private String itemName;
 private String itemValue;
 
 public Item getItem(){
  return this;
 }
 public String getItemName() {
  return itemName;
 }
 public void setItemName(String name) {
  this.itemName = name;
 }
 public String getItemValue() {
  return itemValue;
 }
 public void setItemValue(String value) {
  this.itemValue = value;
 }
}




Here's my test to validate it's working:



public class DozerTest {

 Mapper mapper;
 
 Item item = new Item();
 
 @Before
 public void setup(){
  
  List<String> files = new ArrayList<String>();
  files.add("dozer-bean-mappings.xml");
  mapper = new DozerBeanMapper(files);
  
  item = new Item();
  
  item.setItemName("gear");
  item.setItemValue("Value");
 }
 
 @Test
 public void testEvenMoreNestedMapping(){
  
  ItemHolder itemHolder = new ItemHolder();
  
  itemHolder.setItems(new ArrayList<Item>());
  
  itemHolder.getItems().add(item);
  
  StartDestObject startDestObject = mapper.map(itemHolder, StartDestObject.class);
  
  assertNotNull(startDestObject);
  
  assertEquals(item.getItemName(), startDestObject.getHaveNestedListNoSetter().getHaveListNoSetterList().get(0).getThings().get(0).getName());
  assertEquals(item.getItemValue(), startDestObject.getHaveNestedListNoSetter().getHaveListNoSetterList().get(0).getThings().get(0).getValue());
 }



I'm creating a single Item instance and then set that in the ItemHolder's list.  Then I want to map that Item instance into the Thing class that is deeply nested down at the bottom of my StartDestObject tree.

The trick to allow this deep mapping to work with nested collections that have no set methods is to Not do a deep mapping.  Instead, you break the mappings up into smaller chunks.  You create a mapping that goes from the ItemHolder's item to the StartDestObject's haveNestedListNoSetter property.  That gets us to the first step.  Then Dozer would try to figure out how to map from Item to HaveNestedListNoSetter class.  So, you create a mapping for this which actually just passes the Item (via "this") onto the first item in the HaveNestedListNoSetter's list.  Then you just keep this pattern up, utilizing the "this" to keep passing the Item instance along and then use the is-accessible="true" attribute to access the lists with no set method.

If you're not following my description, here's the dozer mapping to make it all work:

<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://dozer.sourceforge.net
          http://dozer.sourceforge.net/schema/beanmapping.xsd">

  <configuration>
    <stop-on-errors>true</stop-on-errors>
    <wildcard>true</wildcard>
  </configuration>

  <mapping>
    <class-a>com.barry.from.ItemHolder</class-a>
    <class-b>com.barry.to.StartDestObject</class-b>
       <field> 
        <a>items[0]</a>
        <b>haveNestedListNoSetter</b>
        <a-hint>com.barry.from.Item</a-hint> 
      </field>
  </mapping>

   <mapping>
    <class-a>com.barry.from.Item</class-a>
    <class-b>com.barry.to.HaveNestedListNoSetter</class-b>
       <field> 
        <a>this</a>
        <b is-accessible="true">haveListNoSetterList[0]</b>
        <a-hint>com.barry.from.Item</a-hint> 
        <b-hint>com.barry.to.HaveListNoSetter</b-hint>
      </field>
  </mapping>

  <mapping>
    <class-a>com.barry.from.Item</class-a>
    <class-b>com.barry.to.HaveListNoSetter</class-b>
       <field> 
        <a>this</a>
        <b is-accessible="true">things[0]</b>
      <b-hint>com.barry.to.Thing</b-hint>
      </field>
  </mapping>
 
  <mapping>
    <class-a>com.barry.from.Item</class-a>
    <class-b>com.barry.to.Thing</class-b>
    <field> 
        <a>itemName</a>
        <b>name</b>
      </field>
      <field> 
        <a>itemValue</a>
        <b>value</b>
      </field>
  </mapping>
                   
</mappings>



This may make for more mappings, but I don't know of a better solution without creating custom mapping classes or adding setters to the generated code (which sometimes you don't have access to).

If this isn't perfectly clear or you have a better way, please post in the comments.

Thanks!


Wednesday, February 26, 2014

Deploying older JAX-WS applications on WAS 7 or higher (Web Service not recognized)


At my company, we've recently been making a large move from Websphere Application Server(WAS) V6.1 to v8.0.  Some of our web service applications have noticed that if they were exposing JAX-WS service in their app and they simply deployed their app to WAS v8.0, their JAX-WS service was not recognized.  That is there are no errors, but the service is not exposed in anyway.  You can't reach it.

This turns out to be due to the way WAS does the scanning for the JAX-WS annotations.  This article from IBM helps explain it, but I'll give my abbreviated version as well...

Turns out that on WAS 6.1 with the WS Feature pack, WAS would scan your web app for JAX-WS annotations and if a Web Service was found it would expose it properly.  However, as of WAS 7, only Java EE 5 compliant modules are scanned.  That means if you created your app for WAS 6.1, then your web project would have been Java EE 1.4 compatible.  Check out wikipedia for a nice chart of WAS versions and their version support.

Java EE 1.4 means your web module will be version 2.4 where in Java EE 5, your web module would be 2.5.  So, if your web module (war) is not 2.5, then WAS 7 and beyond will not scan it for JAX-WS annotations and therefore your JAX-WS web services will never be exposed.

There are two fixes for this.

1) Upgrade your web module to 2.5 to be Java EE 5 compliant and then WAS 7 and higher will scan it for JAX-WS services.

2) Add a special IBM specific flag in your web applciations META-INF/MANIFEST.MF file like this:
Manifest-Version: 1.0
UseWSFEP61ScanPolicy: true

In the second option, this tell WAS to scan for the JAX-WS annotations even though its an older web module.


If you are using maven you may need to modify your POM file if maven is creating your MANIFEST.MF files.  This stackoverflow post helped us.

Here's what we ended up with:

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<warSourceDirectory>webapp</warSourceDirectory>
<webXml>webapp/WEB-INF/web.xml</webXml>
<archive>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
<manifestEntries>
        <UseWSFEP61ScanPolicy>true</UseWSFEP61ScanPolicy>
      </manifestEntries>
</archive>
</configuration>
</plugin>
...



With this configuration we were able to have maven build our META-INF/MANIFEST.MF file in the war and add the special flag "UseWSFEP61ScanPolicy"

Hope this helps.



Building JAX-WS application with Maven


I was having issues building a JAX-WS application using Maven(NOTE: I'm using maven 2.0.11), when I generated my JAX-WS code using JAX-WS 2.2.  I saw errors like this:

cannot find symbol
symbol  : method required()
location: @interface javax.xml.bind.annotation.XmlElementRef

or

cannot find symbol
symbol  : constructor Service(java.net.URL,javax.xml.namespace.QName,javax.xml.ws.WebServiceFeature[])
location: class javax.xml.ws.Service

This blog really helped me find my solution.

I'll summarize briefly what the issue was.

Java comes with several specifications APIs packaged into it, like JAX-WS & JAXB. However, the version of these specifications are not always the latest.   That is, newer versions of JAX-WS and JAXB may be released, but are not in the version of Java you may be using to compile with.  Even if the newer version is available on your server runtime, they may not be available in your JDK.  And even if you try to explicitly try to add the newer versions of the JAX-WS and JAXB jars to your maven dependency section, it still will not work.

In order to override the built in versions of the JAX-WS and JAXB, you have to utilize the "endorsed" folder.   Follow this link to learn more about the "endorsed" folder.

In short, adding newer versions of the jars to an endorsed folder in your JDK allow them to override any embedded versions.

So, the big question then is how to do this with maven.  Here's the answer..

You use the maven-dependency-plugin to create the "endorsed" folder and copy the newer version of the jars into it.  Then you update the maven-compiler-plugin to refer to this endorsed folder.

Here's a sample POM.


<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<compilerArguments>
<endorseddirs>${project.build.directory}/endorsed</endorseddirs>
</compilerArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/endorsed</outputDirectory>
<silent>true</silent>
<artifactItems>
<artifactItem>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.2</version>
<type>jar</type>
</artifactItem>
<artifactItem>
<groupId>javax.xml.ws</groupId>
<artifactId>jaxws-api</artifactId>
<version>2.2</version>
<type>jar</type>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>


With these settings we were able to successfully build our JAX-WS 2.2 code with Maven.

Hope this helps!




Tuesday, July 23, 2013

JAX-WS Retrieving HTTP Headers


Here's a quick blog on how to retrieve HTTP Headers from a JAX-WS service.  In this example, I'm exposing a web service(I'm providing the service).  I created a JAX-WS handler following these directions


In the handleMessage() method, use the SOAPMessageContext to get the http headers by using the "get" method and passing the  javax.xml.ws.handler.MessageContext's HTTP_REQUEST_HEADERS property.


This will return a Map<String, List<String>>.  Where the key is the http header name and the List<String> is the value... I'm not sure why it's a list, but I just retrieved the first value and got what I expected.

Here's the code:



For the following message(raw view from SOAP UI):

I got the following output in my console:


Hope this helps!  Let me know if you have questions or suggestions!



Tuesday, March 19, 2013

Using Axis2 as JAX-WS Web Service Provider using RAD/Eclipse with Maven



This blog is a little rough... it's more like a journal entry or log of steps I went through trying to use Axis2 to provide a JAX-WS web service.  I actually used IBM's RAD (Rational Application Developer) which is IBM's flavor of Eclipse to do generate the JAX-WS code; however, I then used Axis2 as the web service engine when deploying to Websphere Application Server 6.1.

But hopefully this will be useful to someone, even if they aren't deploying to WAS, but just want an example of how to package Axis2 with their web app or want to see how to use Maven to create the servicejars.    As you'll see, I started with Axis2 1.5 and ran into issues and went back to 1.4 to get things working, but later I updated to 1.6 (when it came out) and things went fine.  So, I'd start with the latest version of Axis2 if possible.

Here's the blow by blow of the steps invovled:


  1. Create an Web Project and Ear
  2. Copy Axis2 jars and such from Axis2 war to web project




  1. Also copy the axis2_web folder into your Webcontent or webapp folder


  1. Change deployment descriptor




  1. Tried publishing to server but got this:

[1/8/10 16:32:15:224 EST] 0000001d SystemErr     R com.ibm.ws.metadata.annotations.AnnotationException: Annotation processing failed for class:  com/ibm/icu/impl/data/LocaleElements_zh__PINYIN.class
[1/8/10 16:32:15:224 EST] 0000001d SystemErr     R at com.ibm.ws.metadata.annotations.AnnotationConfigReader.getAnnotationData(AnnotationConfigReader.java:461)



  1. Remove icu4j-2.6.1.jar from WEB-INF/lib.


  1. Re-publish/Re-start the server and your Axis2 install should be ready to go.


  1. You can verify your Axis2 installation, but running the index.jsp under the WebContent/axis2-web folder.



  1. Then from the Axis2 Welcome page go to the “Validate” link, which will take you to the Axis2 Happiness Page.  Everything should be happy.



  1. Then create a generic project for the WSDL and Schema.


  1. Create a Dynamic Web Project to hold the generated service code.  It has to be a web project only because of the IBM wizard.  We will actually jar it up later and package it under the other web project.  (We actually don’t want this project associated with the EAR, but the wizard will make it associated anyway… So, we will have to undo this..






  1. Now run the Web Service wizard…Right click on the WSDL (from the package Explorer or Project Explorer) and go to Web Services->



  1. Change the Web Service Runtime to JAX-WS.  And select your newly created web project as the Service Project.



  1. On the next screen, I left the defaults:




  1. Click finish.  I got an error because it tried to deploy to my server which does not have the Web Services feature pack.  This is fine because I will be using Axis2 as my JAX-WS runtime and not Websphere’s implementation which comes with the feature pack.




  1. This generated the classes based on the schema and created a service class for us called “StandardizePlaceImpl”.





  1. Modified the StandardizePlaceImpl to return a response based on the input.
  2. Remove the project with the generated code from the EAR’s application XML:



  1. Create a folder in the WEB-INF folder of your Axis2 web project to hold the JAX-WS web service.




  1. JAR up your code and put the WSDL and the schema in the META-INF  folder in the jar.  Used the following pom file to jar up the code and this attempt does not include the WSDL’s and schemas.

<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>com.nw.gis</groupId>
<name>gis web service</name>
<artifactId>gis-ws</artifactId>
<version>1.0</version>
<packaging>jar</packaging>

<properties>
<sourceComplianceLevel>1.5</sourceComplianceLevel>
</properties>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>


<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<inherited>true</inherited>
<configuration>
<source>${sourceComplianceLevel}</source>
<target>${sourceComplianceLevel}</target>
</configuration>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-ws-metadata_2.0_spec</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-jaxws_2.1_spec</artifactId>
<version>1.0</version>
</dependency>
</dependencies>


</project>



  1. Now move the created jar into the servicejars folders under the axis2 folder.



  1. Now refresh the web project and then re-publish and restart the server.

[1/12/10 9:49:25:353 EST] 00000021 ServiceDeploy I org.apache.axis2.deployment.ServiceDeployer deploy Deploying Web service: version-1.5.1.aar - file:/C:/WorkSpaces/RAD7/Axis2_JAXWS/gis-axis-jaxws-ws/WebContent/WEB-INF/services/version-1.5.1.aar
[1/12/10 9:49:25:353 EST] 00000021 JAXWSDeployer I org.apache.axis2.jaxws.framework.JAXWSDeployer deploy Deploying artifact : gis-ws-1.0.jar
[1/12/10 9:49:25:462 EST] 00000021 JAXWSDeployer I org.apache.axis2.jaxws.framework.JAXWSDeployer deployClasses Deploying JAXWS annotated class com.nationwide.gis.service.standardizeplace_1.StandardizePlaceImpl as a service - StandardizePlace.StandardizePlace
[1/12/10 9:49:25:571 EST] 00000021 ServletWrappe I   SRVE0242I: [gis-axis-jaxws-ear] [/gis-axis-jaxws-ws] [AxisServlet]: Initialization successful.



  1. Go back the Axis2 index.jsp and then click Services.



  1. When I click on the service name, which usually shows you the WSDL, I get this error:


<error>
  <description>Unable to generate WSDL 1.1 for this service</description>
  <reason>If you wish Axis2 to automatically generate the WSDL 1.1, then please set useOriginalwsdl as false in your services.xml</reason>
  javax.xml.ws.WebServiceException: Error occurred generating WSDL file for Web service implementation class {com.nationwide.gis.service.standardizeplace_1.StandardizePlaceImpl}: {java.lang.Exception: Error occurred while attempting to read generated schema file {java.lang.RuntimeException: java.net.URISyntaxException: Illegal character in path at index 16: file:/C:/Program Files/IBM/SDP70/runtimes/base_v61/profiles/Axis2JaxWS/temp/NW917514Node10/server1/gis-axis-jaxws-ear/gis-axis-jaxws-ws.war/_axis2com.nationwide.gis.service.standardizeplace_1.StandardizePlaceImpl/StandardizePlace_schema3.xsd}} at org.apache.axis2.jaxws.description.builder.JAXWSRIWSDLGenerator.generateWsdl(JAXWSRIWSDLGenerator.java:187) at org.apache.axis2.jaxws.description.builder.JAXWSRIWSDLGenerator.initialize(JAXWSRIWSDLGenerator.java:371) at org.apache.axis2.jaxws.description.builder.JAXWSRIWSDLGenerator.getWSDL(JAXWSRIWSDLGenerator.java:364) at org.apache.axis2.description.AxisService.printWSDL(AxisService.java:1314) at org.apache.axis2.transport.http.ListingAgent.processListService(ListingAgent.java:270) at org.apache.axis2.transport.http.AxisServlet.doGet(AxisServlet.java:249) at javax.servlet.http.HttpServlet.service(HttpServlet.java:743) at javax.servlet.http.HttpServlet.service(HttpServlet.java:856) at



  1. When I use SOAP UI and point it to a local copy of the WSDL and invoke the service I get this:
1/12/10 11:05:25:080 EST] 00000020 FactoryRegist E org.apache.axis2.jaxws.registry.FactoryRegistry <clinit> null
                                java.lang.ExceptionInInitializerError
at java.lang.J9VMInternals.initialize(J9VMInternals.java:214)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:258)
at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:139)
at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:117)
Caused by: java.lang.ClassCastException: org.apache.xerces.jaxp.datatype.DatatypeFactoryImpl incompatible with javax.xml.datatype.DatatypeFactory
at javax.xml.datatype.DatatypeFactory.newInstance(Unknown Source)
at com.sun.xml.bind.DatatypeConverterImpl.<clinit>(DatatypeConverterImpl.java:783)
at java.lang.J9VMInternals.initializeImpl(Native Method)
at java.lang.J9VMInternals.initialize(J9VMInternals.java:192)
... 47 more


  1. Noticed that the class it was complaining about was in the xercesImpl-2.8.1.jar which comes with Axis2 1.4, but my Axis2 1.5 version came with an older version of xercesImpl-2.6.2.jar.  So, I replaced my xerces jar with the newer version.  I believe it was using an implementation from IBM and not from the Axis2 jars since there was no implmentation in the Axis libs.


  1. After this change it worked fine for the first request submitted, but then I got an error on any more requests that I submitted:


[1/12/10 13:53:21:080 EST] 0000001d AxisEngine    E org.apache.axis2.engine.AxisEngine receive javax.xml.bind.UnmarshalException
- with linked exception:
[javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.nationwide.com/schemas/iaa7_1/customerrelationmanagement/GIS/placeInformationManagement_1", local:"standardizePlace"). Expected elements are (none)]
                                org.apache.axis2.AxisFault: javax.xml.bind.UnmarshalException
- with linked exception:
[javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.nationwide.com/schemas/iaa7_1/customerrelationmanagement/GIS/placeInformationManagement_1", local:"standardizePlace"). Expected elements are (none)]
at org.apache.axis2.AxisFault.makeFault(AxisFault.java:430)
at org.apache.axis2.jaxws.server.JAXWSMessageReceiver.receive(JAXWSMessageReceiver.java:218)
at org.apache.axis2.engine.AxisEngine.receive(AxisEngine.java:173)
...
Caused by: javax.xml.ws.WebServiceException: javax.xml.bind.UnmarshalException
- with linked exception:
[javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.nationwide.com/schemas/iaa7_1/customerrelationmanagement/GIS/placeInformationManagement_1", local:"standardizePlace"). Expected elements are (none)]
at org.apache.axis2.jaxws.ExceptionFactory.createWebServiceException(ExceptionFactor
... 25 more


  1. Let’s see if we can turn on any logging using Log4J.  So, I added a commons-logging.properties file to the classpath with the following lines:

org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger
priority=999


  1. Then I added a log4j.properties file, and turned on debug level logging on the root logger.


  1. Re-ran the test… Got this for the second request which errored out… (note second request is same as first):

[1/12/10 15:10:25:795 EST] 00000020 SystemOut     O [01/12/2010 15:10:25,795]JAXBUtils DEBUG: This classloader will be used to construct the JAXBContext
com.ibm.ws.classloader.CompoundClassLoader@34043404
  Local ClassPath: C:\WorkSpaces\RAD7\Axis2_JAXWS\gis-axis-jaxws-ws\WebContent\WEB-INF\classes;C:\WorkSpaces\RAD7\Axis2_JAXWS\gis-axis-jaxws-ws\WebContent\WEB-INF\lib\activation-1.1.jar;C:\WorkSpaces\RAD7\Axis2_JAXWS\gis-axis-jaxws-ws\WebContent\WEB-INF\lib\antlr-2.7.7.jar;C:\WorkSpaces\RAD7\Axis2_JAXWS\gis-axis-jaxws-ws\WebContent\WEB-...
<removed cause it was really long>
[1/12/10 15:10:25:795 EST] 00000020 SystemOut     O [01/12/2010 15:10:25,795]JAXBUtils DEBUG: checking package :com.nationwide.schemas.iaa7_1.customerrelationmanagement.gis.placeinformationmanagement_1
[1/12/10 15:10:25:795 EST] 00000020 SystemOut     O [01/12/2010 15:10:25,795]JAXBUtils DEBUG: Exception thrown from AccessController: java.security.PrivilegedActionException: java.lang.ClassNotFoundException: com.nationwide.schemas.iaa7_1.customerrelationmanagement.gis.placeinformationmanagement_1.ObjectFactory
[1/12/10 15:10:25:795 EST] 00000020 SystemOut     O [01/12/2010 15:10:25,795]JAXBUtils DEBUG: ObjectFactory Class Not Found java.lang.ClassNotFoundException: com.nationwide.schemas.iaa7_1.customerrelationmanagement.gis.placeinformationmanagement_1.ObjectFactory
[1/12/10 15:10:25:795 EST] 00000020 SystemOut     O [01/12/2010 15:10:25,795]JAXBUtils DEBUG: Exception thrown from AccessController: java.security.PrivilegedActionException: java.lang.ClassNotFoundException: com.nationwide.schemas.iaa7_1.customerrelationmanagement.gis.placeinformationmanagement_1.package-info
[1/12/10 15:10:25:795 EST] 00000020 SystemOut     O [01/12/2010 15:10:25,795]JAXBUtils DEBUG: package-info Class Not Found java.lang.ClassNotFoundException: com.nationwide.schemas.iaa7_1.customerrelationmanagement.gis.placeinformationmanagement_1.package-info
[1/12/10 15:10:25:795 EST] 00000020 SystemOut     O [01/12/2010 15:10:25,795]JAXBUtils DEBUG: Package com.nationwide.schemas.iaa7_1.customerrelationmanagement.gis.placeinformationmanagement_1 does not contain an ObjectFactory or package-info class.  Searching for JAXB classes
[1/12/10 15:10:25:795 EST] 00000020 SystemOut     O [01/12/2010 15:10:25,795]JAXBUtils DEBUG: getClassesFromJarFile failed to get Classes


  1. Replaced all Axis2 1.5 jars and folders under WEB-INF with Axis2 1.4 versions and the service works… I have issues getting Axis2 to display the WSDL, but invoking the service works just fine.


  1. I get the following error when trying to click on the Service name to see the WSDL…

[2/15/10 12:07:08:217 EST] 00000020 SystemOut     O [02/15/2010 12:07:08,217]JAXWSRIWSDLGenerator ERROR: Error occurred generating WSDL file for Web service implementation class {com.nationwide.gis.service.standardizeplace_1.StandardizePlaceImpl}: {java.lang.reflect.InvocationTargetException}
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:615)
at org.apache.axis2.jaxws.description.builder.JAXWSRIWSDLGenerator.generateWsdl(JAXWSRIWSDLGenerator.java:173)
at org.apache.axis2.jaxws.description.builder.JAXWSRIWSDLGenerator.initialize(JAXWSRIWSDLGenerator.java:371)
at org.apache.axis2.jaxws.description.builder.JAXWSRIWSDLGenerator.getWSDL(JAXWSRIWSDLGenerator.java:364)
at org.apache.axis2.description.AxisService.printWSDL(AxisService.java:1322)
at ...
Caused by: java.lang.ExceptionInInitializerError
at java.lang.J9VMInternals.initialize(J9VMInternals.java:214)
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:219)
at ...
... 33 more
Caused by: java.lang.ClassCastException: org.apache.xerces.jaxp.datatype.DatatypeFactoryImpl incompatible with javax.xml.datatype.DatatypeFactory
at javax.xml.datatype.DatatypeFactory.newInstance(Unknown Source)
at com.sun.xml.bind.DatatypeConverterImpl.<clinit>(DatatypeConverterImpl.java:740)
at java.lang.J9VMInternals.initializeImpl(Native Method)
at java.lang.J9VMInternals.initialize(J9VMInternals.java:192)



UPDATE: 03/23/2012:

Did some more testing with this approach again, and found that I could follow most of this document and get a web service working with RAD 8 and Axis2 1.6.0.

I generated the code in RAD using the WSDL and JAX-WS.  Then created a Dynamic Web Project that I then copied the Axis2 stuff into… all but the libs.  We should be using maven to populate the libs (or m2Eclipse plugin).  I had Axis2 1.6.0 added to our internal enterprise repo last year and found that for most JAX-WS services you just need to add the following dependencies:

<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-kernel</artifactId>
<version>${axis2Version}</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.neethi</groupId>
<artifactId>neethi</artifactId>
</exclusion>

</exclusions>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-transport-http</artifactId>
<version>${axis2Version}</version>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-transport-local</artifactId>
<version>${axis2Version}</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-adb</artifactId>
<version>${axis2Version}</version>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-jaxws</artifactId>
<version>${axis2Version}</version>
</dependency>
<dependency>
<groupId>org.apache.neethi</groupId>
<artifactId>neethi</artifactId>
<version>2.0.5</version>
</dependency>


This should pull in the jars necessary to use JAX-WS with Axis2.

Also, I used the following plugin configuration in the WAR pom to pull in the generated code into the WEB-INF/servicejars folder.

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<webResources>
<resource>
  <!-- this is relative to the pom.xml directory -->
      <directory>../spell-check-ws-gen/target</directory>
      <targetPath>WEB-INF/servicejars</targetPath>
      <includes>
    <include>spell-check-ws-gen*</include>
  </includes>
    </resource>
</webResources>
</configuration>
</plugin>


The “spell-check-ws-gen” project is where I had the generated JAX-WS code.  This plug-in configuration was in the Dynamic Web Project that I created and copied the Axis2 stuff into (stuff = web.xml, axis2-web, servicejars folder, modules, etc… but not the lib)

As long as my spell-check-ws-gen project was one of the modules referred to by the build project’s pom, then it would get built and then when the war project got built this plugin would run and pull that jar into the servicejars folder where Axis2 expects it.

At first I put the JAX-WS generated code jar only in the servicejars folder, but then I would get this error:

AxisEngine    E org.apache.axis2.engine.AxisEngine receive org.apache.axis2.jaxws.wrapper.impl.JAXBWrapperException: java.lang.IllegalArgumentException: argument type mismatch
                                org.apache.axis2.AxisFault: org.apache.axis2.jaxws.wrapper.impl.JAXBWrapperException: java.lang.IllegalArgumentException: argument type mismatch


I found that if I added the jar with the generated JAX-WS code(that is the spell-check-ws-gen in my case) also to the WEB-INF/lib, then this error would go away.

So, this jar would be in two places… in the WEB-INF/lib folder and in the WEB-INF/servicejars folder.  Then the service would work fine.

The Axis2 services page was not be able to produce a wsdl for the service, but I didn’t care.