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!


Thursday, July 3, 2014

Overriding WS-Addressing headers in JAX-WS


This is going to be quick and to the point, let's suppose you have a web service you have to call that requires WS-Addressing headers.  Usually, if the WSDL is created with the <wsaw:UsingAddressing wsdl:required="true"/> under the binding section, then the generated JAX-WS client will automatically add the WS-Addressing headers for you.

The problem I've had is that with the IBM Websphere runtime, you have no easy way to override the default parameters.  I've tried many different ways, but was only able to get one way to work (which I'll share in just a minute).  I originally thought it would be pretty simple... just create a handler that intercepts the message as it's going out and modify the appropriate SOAP Header element.  But the problem with this approach is that (at least in the IBM runtime) the WS-Addressing header information isn't created and added to the soap request until after it goes through the handler chains.  So, in a handler, you won't see any of the WS-Addressing headers because they haven't been added yet.  However, I did find that you if you add your own WS-Addressing header in a handler, the JAX-WS runtime won't try to create it.  So, I ended up creating a handler and just creating the WS-Addressing header I cared about overriding and set it there.

Here's the handler code:


 @Override  
      public boolean handleMessage(SOAPMessageContext smc) {  
           Boolean outBoundProperty = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);  
           if (outBoundProperty.booleanValue()) {  
                SOAPFactory factory = null;  
                javax.xml.soap.SOAPElement element = null;  
                try {  
                     factory = SOAPFactory.newInstance();  
                     element = factory.createElement(new QName("http://www.w3.org/2005/08/addressing", "To"));  
                     element.setTextContent("IT");  
                     // check if there's a SOAP Header  
                     SOAPHeader header = smc.getMessage().getSOAPPart().getEnvelope().getHeader();  
                     if (header == null) {  
                          // create header since there isn't one  
                          header = smc.getMessage().getSOAPPart().getEnvelope().addHeader();  
                     }  
                     header.addChildElement(element);  
                } catch (SOAPException e) {  
                     // TODO: Do better error handling and logging  
                     e.printStackTrace();  
                }  
           }  
           return true;  
      }  

This handler creates a "To" element then adds it to the header.  It makes sure there's a header to add it to first.

This will produce a header like this:

   <soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
      <To xmlns="http://www.w3.org/2005/08/addressing">IT</To>
      <wsa:MessageID>urn:uuid:e180d2ff-7254-45eb-8bd8-f0058bb61ec7</wsa:MessageID>
      <wsa:Action>http://ws.cdyne.com/CheckTextBody</wsa:Action>
   </soapenv:Header>


With a handler like this you can easily override the parameters.  I'd be interested to hear of other ways.  Perhaps easier ways of doing the same.  Or let me know what you find out.  My experience is with Websphere 8.




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!