Tuesday, January 29, 2013

Java assist to the rescue

Java assist to the rescue We use XStream (http://xstream.codehaus.org/) for serializing objects to xml. Our basic structure is:

 
 
 
  
  
 

We create annotations on our objects for the XStream converter, which worked fine. The problem we faced was how to give a proper name for the “object” node in the xml. Since we needed objects for the converter, we created a class GlobalInfo that accepted a base class of GenericObject. Each scenario that needed to transform an object to the xml, we inherited the GenericObject that had an internal field by the name that we wanted, this way the standard serializer used the field name for the xml. For example:
 public class TempObject extends GenericObject {
 private MyObject myName;
}
This would create the following xml: I looked for a generic way to create the “myName” node. Using XStream the standard way was to use annotations. The problem is that during run time you cannot change the annotation values. To overcome this I used java assit. With java assit I could create the class “TempObject” by code during runtime and then I would have a generic way to create the xml node without creating multiple classes during the coding phase. The idea is to create a class during runtime, and then assign the filed myName with the object that needs to be serialized. The following example will create the class:
@SuppressWarnings({ "rawtypes", "unchecked" })
public static GenericObject generate(Object source, String objectFieldName) {
 try {
  ClassPool pool = ClassPool.getDefault();
  // create the class
  String generatedClassName = String.format("TempObject.%s", objectFieldName);
  Class clazz = classFactory.get(objectFieldName);
  if (clazz == null) {
   CtClass transClass = null;
   try {
    transClass = pool.get(generatedClassName);
   } catch (NotFoundException e) {

    transClass = pool.makeClass(generatedClassName);
    CtClass superClass = pool.get("a.b.c.GenericObject");
    transClass.setSuperclass(superClass);

    CtField field = CtField.make(
      String.format("private Object %s;", objectFieldName), transClass);
    transClass.addField(field);
    transClass.addMethod(CtNewMethod.make(
      String.format("public void setObject (Object obj) { this.%s=obj; }",
        objectFieldName), transClass));

    clazz = transClass.toClass();
    classFactory.put(objectFieldName, clazz);
   }
  }

  Object obj = clazz.newInstance();
  Class[] formalParams = new Class[] { Object.class };
  Method meth = clazz.getDeclaredMethod("setObject", formalParams);
  meth.invoke(obj, source);
  // PropertyUtils.setProperty(obj, "obj", source);
  return (GenericObject) obj;

 } catch (Exception e) {
  throw new RuntimeException(e);

 }
}

Wednesday, January 23, 2013

Java Web Service Client


Java Web Service Client

There are numerous ways to create web service clients in java. The simplest way is of course to create a proxy class from the wdsl (see http://sourceforge.net/projects/wsdl2javawizard/, http://axis.apache.org/axis2/java/core/tools/eclipse/wsdl2java-plugin.html).
There are times that this is fine usually when you need to support only one web service and it is static. The downside is that a web service is just a protocol for communication, and we have now coupled our code to this specific web service and its interface.

First level of abstraction: spring templates

Spring has a template class for multiple services. The class for this is: WebServiceTemplate. This class supports defaultUri if all your calls are to the same web service.
The simplest function of this class is:
boolean sendSourceAndReceiveToResult(String uri, Source requestPayload, WebServiceMessageCallback requestCallback, final Result responseResult);

Through this function you can send to any web service. You define the url and the xml payload for the service. Spring will add all the soap headers needed (except for the soap action – we will see this later on).

An example:

String url = "http://www.w3schools.com/webservices/tempconvert.asmx";
String xml =” <CelsiusToFahrenheit xmlns="http://tempuri.org/"> <Celsius>22</Celsius>
</CelsiusToFahrenheit>”;

StreamSource webServiceInput = new StreamSource(new StringReader(xmlDoc));
StringWriter finalResponseWriter = new StringWriter();

StreamResult webServiceOutput = new StreamResult(finalResponseWriter);
webServiceTemplate.sendSourceAndReceiveToResult(url, webServiceInput,
                           webServiceOutput);

If succeeded the result of the soap request is stored in the finalResponseWriter object.

The soap action needs to be added to the soap request, and cannot be automatically added. To add support for this there is the callback method. For example:

new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message) {
// Please see the WSDL for more details.
 ((SoapMessage) message).setSoapAction(“http://tempuri.org/CelsiusToFahrenheit”);
}

A more generic approach can be added by the following function:
public boolean sendSourceAndReceiveToResult(String uri,
              Source requestPayload, final Result responseResult,
              final String soapAction) {

       return super.sendSourceAndReceiveToResult(uri, requestPayload,
              new WebServiceMessageCallback() {
                     public void doWithMessage(WebServiceMessage message) {
                           // Please see the WSDL for more details.
                           ((SoapMessage) message).setSoapAction(soapAction);
                     }
              }, responseResult);
}


The spring template will allow the beginning of decoupling. We do not create specific classes for the web service, and if the provider is changed the we can paramitize the web service properties to an ini file and the xml request to another class to generate per web service interface.

Second level of abstraction: spring integration

With the soap template we still need write an abstraction level to create the xml requests per web service client.  In addition if the web service needs to be changed with another protocol the code now needs to be redone. To get full decoupling model we will use spring integration. To fully understand spring integration see http://www.springsource.org/spring-integration.

To make the code as nice as possible as in the example above we want to create a class CelsiusToFahrenheit and have the option to send a request to a service to get the result. What needs to be done is to serialize the class to the specific service protocol. This abstraction can be done with spring integration.
First we will look at the code that sends the class to get the Celsius to Fahrenheit conversion.

CelsiusToFahrenheit cToF = new CelsiusToFahrenheit(22);
                          
MessagingTemplate messagingTemplate = new MessagingTemplate(celsiusToFahrenheit);
              Message<?> response = messagingTemplate.sendAndReceive(new GenericMessage<CelsiusToFahrenheit>(cToF));
CelsiusToFahrenheitResponse celsiusToFahrenheitResponse = (CelsiusToFahrenheitResponse)response.getPayload();

To accomplish this we need to convert the CelsiusToFahrenheit class to xml using jaxb. We then send the xml on the web service. The result of the web service is then unmarshalled from xml to the class CelsiusToFahrenheitResponse.

The spring integration for this is:

<bean id="c2fMarshallingTransformer"
       class="org.springframework.integration.xml.transformer.MarshallingTransformer">
       <constructor-arg>
              <bean class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
                     <property name="classesToBeBound">
                           <array>
                                  <value> CelsiusToFahrenheit
                                  </value>
                           </array>
                     </property>
              </bean>
       </constructor-arg>
       <constructor-arg>
              <bean
                     class="org.springframework.integration.xml.transformer.ResultToDocumentTransformer" />
       </constructor-arg>
</bean>

<bean id="c2fUnmarshallingTransformer"
       class="org.springframework.integration.xml.transformer.UnmarshallingTransformer">
       <constructor-arg>
              <bean class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
                     <property name="classesToBeBound">
                           <array>
                                  <value>CelsiusToFahrenheitResponse
                                  </value>
                           </array>
                     </property>
              </bean>
       </constructor-arg>
</bean>


<int:channel id="celsiusToFahrenheit" />
<int:chain input-channel="celsiusToFahrenheit">
       <int:transformer ref="c2fMarshallingTransformer" />
       <ws:header-enricher>
              <ws:soap-action value="http://tempuri.org/CelsiusToFahrenheit" />
       </ws:header-enricher>
       <ws:outbound-gateway
              uri="http://www.w3schools.com/webservices/tempconvert.asmx">
       </ws:outbound-gateway>
       <int:transformer ref="c2fUnmarshallingTransformer" />
</int:chain>


Other issues with Soap Requests

Whether using spring integration or spring template there are issues with web services that need to be overcome.

SSL – with or without credentials

The first is to allow sending web services over ssl and might or might not include using user credentials. To do this spring has two implementations for the sending of the web service. One is HttpComponentsMessageSender. This implementation uses HttpClient as the underlying connection. The HttpClient has many features that we will use.
By using the url of https, the HttpComponentsMessageSender knows to send all requests via ssl. To use user credentials the following needs to be configured on the message sender object:


<bean id="messageSender"
       class="org.springframework.ws.transport.http.HttpComponentsMessageSender">
       <property name="connectionTimeout" value="3000" />
       <property name="readTimeout" value="5000" />
       <property name="credentials">
              <bean class="org.apache.http.auth.UsernamePasswordCredentials">
                     <constructor-arg value="username:password" />
              </bean>
       </property>
</bean>


Timeout

To add timeout parameters we also use the HttpClient as above and will configure:
<bean id="messageSender"
       class="org.springframework.ws.transport.http.HttpComponentsMessageSender">
       <property name="connectionTimeout" value="3000" />
       <property name="readTimeout" value="5000" />
</bean>


Retries

We will again use the built in options of the HttpClient.
HttpClient support retry by using the following interface:

public interface HttpRequestRetryHandler {
    boolean retryRequest(IOException exception, int executionCount, HttpContext context);
}

You can implement this in your own way if needed. There is a default retry handler: DefaultHttpRequestRetryHandler.
The default handler will retry 3 times if not succeeded. The default handler knows to distinguish between a failed connection (and will retry) to other failures that retry will not help (like UnknownHostException, ConnectException, SSLException, InterruptedIOException). In the case that the exception is not in the list above the default handler calls the function handleAsIdempotent to determine if it is a read only request.
In the case of web services all http requests are post, and the protocol does not notify if the request is read only or not. So we will add a new class SoapHttpRequestRetryHandler that will override the handleAsIdempotent and allow to configure in the xml file the read only soap actions.

public class SoapHttpRequestRetryHandler extends DefaultHttpRequestRetryHandler {

       private List<String> readOnlySoapActions = new LinkedList<String>();
      
       public SoapHttpRequestRetryHandler(int retryCount, boolean requestSentRetryEnabled, List<String> readOnlySoapActions){
              super(retryCount, requestSentRetryEnabled);
              this.readOnlySoapActions = readOnlySoapActions;
       }

      
       private String getSoapAction(HttpRequest request){
              return request.getFirstHeader("SOAPAction").getValue();
       }
      
       @Override
       protected boolean handleAsIdempotent(HttpRequest request) {
              if (readOnlySoapActions.indexOf(getSoapAction(request))!=-1)
                     return true;
              return super.handleAsIdempotent(request);
       }
      
}

Untrusted SSL Clients


In the case of a trusted site, spring soap template knows to use the sites certificate to encrypt all data. If the site is not trusted, then spring will raise an exception:
SunCertPathBuilderException: unable to find valid certification path to requested target.

In the case you do not want this, you need to register a new class SSLSocketFactory with the httpclient, and add the method:

public boolean isTrusted(final X509Certificate[] chain, String authType) throws CertificateException {
return true;
}

For example:

private static DefaultHttpClient trustEveryoneSslHttpClient() {
    try {
        SchemeRegistry registry = new SchemeRegistry();

        SSLSocketFactory socketFactory = new SSLSocketFactory(new TrustStrategy() {

            public boolean isTrusted(final X509Certificate[] chain, String authType) throws CertificateException {
                // Oh, I am easy...
                return true;
            }

        }, org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        registry.register(new Scheme("https", 443, socketFactory));
        registry.register(new Scheme("http", 80, new PlainSocketFactory()));
       
        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(registry);
        DefaultHttpClient client = new DefaultHttpClient(mgr, new DefaultHttpClient().getParams());
        return client;
    } catch (GeneralSecurityException e) {
        throw new RuntimeException(e);
    }
}     

Monday, January 14, 2013

REST WADL / Extending WadlGenerator in CXF




A problem in the world of wadl, is that the documentation level on the wadl level is not great.
An example:
<resource path="/agents">
                <method name="GET">
                                <request>
                                                <param name="externalAgentRef" style="query" type="xs:string"/>
                                </request>
                                <response>
                                                <representation mediaType="application/json"/>
                                </response>
                </method>
</resource>

In this example you can see that the default WadlGenerator will display all parameters in the input, but the output is defined as application/json. What we want is for the xml to describe the object that is serialized on the output.
Solution:
To do this we need do define a provider for the jaxrs:
<jaxrs:providers>
<bean class="...WadlGeneratorEx">
<property name="linkJsonToXmlSchema" value="true" />
</bean>                                 
</jaxrs:providers>

CXF does not have a standard interface for the Wadl generator, so you need to extend the current WadlGenerator (http://cxf.apache.org/javadoc/latest/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.html).
Since the class was not created properly for overriding methods, I copied to whole class to a new class with the inheritance of:
public class WadlGeneratorEx extends WadlGenerator implements RequestHandler
On the method: private void handleRepresentation, I added the following line:
sb.append(" classImpl=\"").append(type.getSimpleName()).append("\"");

The new output looks like:
<representation mediaType="application/json" classImpl="Agent"/>

You can now add any other xml attributes you need for documentation.