Showing posts with label Web Services. Show all posts
Showing posts with label Web Services. Show all posts

Thursday, March 21, 2013

JAXWS Handler injection using Spring

I'll try to keep this post less verbose and let the code speak for itself.

In order inject the handler using Spring, we need the below mentioned default handler resolver class and the configuration.

DefaultHandlerResolver
package handler;

import java.util.List;

import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.HandlerResolver;
import javax.xml.ws.handler.PortInfo;

@SuppressWarnings("rawtypes")
public class DefaultHandlerResolver implements HandlerResolver {
 private List<Handler> handlerList;

 public List<Handler> getHandlerChain(PortInfo portInfo) {
  return handlerList;
 }

 public void setHandlerList(List<Handler> handlerList) {
  this.handlerList = handlerList;
 }
}


applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

 <!-- Web Service custom properties (Timeouts in millis)  -->
 <util:map id="jaxwsCustomProperties">
  <entry key="com.sun.xml.ws.connect.timeout">
   <value type="java.lang.Integer">15000</value>
  </entry> 
  <entry key="com.sun.xml.ws.request.timeout">
   <value type="java.lang.Integer">15000</value>
  </entry>
 </util:map>

 <bean id="handlerResolver" class="handler.DefaultHandlerResolver">
  <property name="handlerList">
   <list>
    <bean class="handler.SOAPRequestResponseSpitter" />
   </list>
  </property>
 </bean>

 <bean id="calculatorServicePortType" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
  <property name="serviceInterface" value="service.Calculator" />
  <property name="wsdlDocumentUrl" value="CalculatorService.wsdl" />
  <property name="namespaceUri" value="http://service/" />
  <property name="serviceName" value="CalculatorService" />
  <property name="endpointAddress" value="http://localhost:7001/CalculatorServiceWebApp/CalculatorService" />
  <property name="customProperties" ref="jaxwsCustomProperties" />
  <property name="handlerResolver" ref="handlerResolver"/>
 </bean>
</beans>


Once the above files are ready, then run the following in the main method of your test class or JUnit:
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "applicationContext.xml" });
Calculator calculator = (Calculator) context.getBean("calculatorServicePortType");
System.out.println("Calculated result [" + calculator.add(2, 3) +"]");


The output should look like: Outbound message:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
  <S:Header/>
  <S:Body>
    <ns2:add xmlns:ns2="http://service/">
      <a>2</a>
      <b>3</b>
    </ns2:add>
  </S:Body>
</S:Envelope>
Inbound message:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
 <S:Header/>
 <S:Body>
  <ns2:addResponse xmlns:ns2="http://service/">
   <return>5</return>
  </ns2:addResponse>
 </S:Body>
</S:Envelope>
Calculated result [5]

JAXWS Handler : Example for logging request / response SOAP packets

There are times when we want to see the request / response SOAP packets in our system console or log files. Instead of going for TCP monitor or other similiar tools, I though of writing a handler. The code below is quite popular and can be found in many other posts too.

Adding this handler is simple. Just pass the binding object of your Web Service client port into the static addToPort(...) method in this class.
package handler;

import java.io.ByteArrayOutputStream;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import javax.xml.namespace.QName;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.Binding;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;

public class SOAPRequestResponseSpitter implements SOAPHandler<SOAPMessageContext> {

 @Override
 public boolean handleMessage(SOAPMessageContext context) {
  logToSystemOut(context);
  return true;
 }

 @Override
 public boolean handleFault(SOAPMessageContext context) {
  logToSystemOut(context);
  return true;
 }

 private void logToSystemOut(SOAPMessageContext smc) {
  Boolean outboundProperty = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);

  if (outboundProperty.booleanValue()) {
   System.out.println("\nOutbound message:");
  } else {
   System.out.println("\nInbound message:");
  }

  SOAPMessage message = smc.getMessage();
  try {
       ByteArrayOutputStream baos = new ByteArrayOutputStream();
       message.writeTo(baos);
       System.out.println(baos.toString());
  } catch (Exception e) {
   System.out.println("Exception in handler: " + e);
  }
 }

 @Override
 public void close(MessageContext context) {
 }

 @Override
 public Set<QName> getHeaders() {
  return Collections.emptySet();
 }

 @SuppressWarnings("rawtypes")
 /**
  * This static method adds the handler to the provided port's binding object. 
  * 
  * @param binding - The binding object can be fetched by <code>((BindingProvider) port).getBinding()</code>
  */
 public static void addToPort(Binding binding) {
  List<Handler> handlerChain = binding.getHandlerChain();
  handlerChain.add(new SOAPRequestResponseSpitter());

  /*
   * Check List<Handler> javax.xml.ws.Binding.getHandlerChain() javadocs.
   * It states: Gets a copy of the handler chain for a protocol binding
   * instance. If the returned chain is modified a call to setHandlerChain
   * is required to configure the binding instance with the new chain.
   */
  binding.setHandlerChain(handlerChain);
 }
}


To inject this handler into the port using Spring, please refer to my other post JAXWS Handler injection using Spring

The handler gives you much control on how you want to log the packets. If you don't want to write a handler and just want to see the packets without making any changes to the application, you can use the below mentioned JVM argument in your server startup script and all the web service related tcp communication will be printed on the console.
  -Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true

Monday, May 16, 2011

Setting timeout for web service invocations using SII client generated using Weblogic 8.1 SP6 ANT task

Even though the 11g version of weblogic has been released, most of the big organizations continue to use BEA Weblogic 8.1 version (there are number of reasons and I won't get into details). Since we have been invoking some very busy web services and wanted to utilize the timeout mechanism to throw an exception on the client side so that the user may be requested to try at a later time rather than waiting for ever. Over a number of blogs I saw people suggesting the use of weblogic.webservice.rpc.timeoutsecs property and provide the timeout values in seconds. It didn't seem to work for me and so was the experience of many users using the webservice client generated by weblogic ant tasks and using the weblogic.jar and webservice.jar in classpath. After a number of google searches, I found out a person who had succesfully used the above mentioned property to get the timeout functionality working. The secret to this person's solution was, he had used a DII (i.e. used a self written client instead of the generated client). The sample DII client for a Calculator service with an add webmethod is as below:
      String NS_XSD = "http://www.w3.org/2001/XMLSchema";
      String targetNamespace = "http://www.bea.com/examples/Calculator";

      System.setProperty("javax.xml.rpc.ServiceFactory", "weblogic.webservice.core.rpc.ServiceFactoryImpl");
      System.setProperty("weblogic.webservice.verbose", "false");
      System.setProperty("weblogic.webservice.UseWebLogicURLStreamHandler", "true");

      ServiceFactory factory = ServiceFactory.newInstance();
      QName serviceName = new QName(targetNamespace, "CalculatorService");
      QName portName = new QName(targetNamespace, "CalculatorServicePort");
      QName operationName = new QName(targetNamespace, "add");
      Service service = factory.createService(serviceName);

      Call call = service.createCall();
      call.setOperationName(operationName);
      call.setPortTypeName(portName);
      call.setProperty(Call.OPERATION_STYLE_PROPERTY, "rpc");

      call.addParameter("intVal", new QName(NS_XSD, "int"), Integer.class, ParameterMode.IN);
      call.addParameter("intVal0", new QName(NS_XSD, "int"), Integer.class, ParameterMode.IN);
      call.setReturnType(new QName(NS_XSD, "int"), Integer.class);
      call.setTargetEndpointAddress("http://localhost:7001/CalculatorService/CalculatorService");
      ((weblogic.webservice.core.rpc.CallImpl) call).setProperty("weblogic.webservice.rpc.timeoutsecs", "5");
      System.out.println("Invoked DII :");
      System.out.println("Result : " + call.invoke(new Object[] { new Integer(a), new Integer(b) }));
Now comes the second problem, all the existing client code for a huge number of webservices are SII codes (generated by the ANT task in webservices.jar). Instead of converting all the client codes to DII, I had to find the solution using SII. Since the code is not provided by BEA, I decompiled the jar to understand what's going on under the hood and found something strange, the weblogic.webservice.rpc.timeoutsecs set as a system property was fetched inside the jared code (in webservices.jar) but was only used in the logic that handled https invocations (may be missed out looking at the right place or it was never there). In order to confirm my findings, I exposed the service over https and modified the client code with some additional properties (related to https invocations and nothing to do with web service in particular) and VOILA!!!! It worked :)

Inference: To use the timeout property for rpc calls when using client generated SII, the webservice should be exposed in https (can't change the code in the jars used by the client code to fix it as the code is not open source). Or write a DII (example given above). For SII invocation over https you need to use the below mentioned properties:
       
System.setProperty("weblogic.security.SSL.trustedCAKeyStore", "C:/bea/weblogic81/server/lib/DemoIdentity.jks");
System.setProperty("weblogic.webservice.client.ssl.strictcertchecking", "false");
System.setProperty("weblogic.webservice.rpc.timeoutsecs", "10");
For invocations over https you shall need jsafeFIPS.jar & webserviceclient+ssl.jar in classpath in addition to webservices.jar & weblogic.jar (you can find all of those in the weblogic's lib folder). The invocation may also ask for license files (don't know why), then even put the license.bea & license_scale_limited.bea in the classpath.

For Weblogic 9 versions, the property to be set has changed. More details can be found at http://download.oracle.com/docs/cd/E13222_01/wls/docs92/webserv/client.html. Those properties are:
((Stub)service_action)._setProperty("weblogic.wsee.transport.read.timeout", 5000); //values are in millis
((Stub)service_action)._setProperty("weblogic.wsee.transport.connection.timeout", 5000); //values are in millis
Similiar properties for Weblogic 10 can be found at http://docs.oracle.com/cd/E13222_01/wls/docs100/webserv/client.html I hope you found this post helpful.

Thursday, June 17, 2010

Which style of WSDL should I use?

A Web Services Description Language (WSDL) binding style can be RPC or document. The use can be encoded or literal. How do you determine which combination of style and use to use? The author describes the WSDL and SOAP messages for each combination to help you decide.

Which style of WSDL should I use?

Thursday, September 18, 2008

Solution to the issue with List's in wsimport generated POJO classes

This topic is in extension to " It is not necessary to use wsimport generated POJO Files " post on this blog. Lets consider that our Employee class has another property called addresses . This addresses property is a List of strings. So our modified Employee shall look like:
import java.util.List;

public class Employee {
 private int age;
 private String name;
 private List<String> addresses;

 public Employee() {
 }

 public Employee(int age, String name, List<String> addresses) {
  super();
  this.age = age;
  this.name = name;
  this.addresses = addresses;
 }
 
 //write getter setter for properties to comply to Java Bean/POJO standard
}
and the Web Method is:
@WebMethod
 public Employee printEmployee(Employee e) {
  System.out.println(e.getName() + " - " + e.getAge() + " - " e.getAddresses);
  return e;
 }
As a result of generating the client using wsimport, the generated Employee looks like:
@XmlAccessorType(XmlAccessType.FIELD) 
 @XmlType(name = "employee", propOrder = {  "addresses",  "age",  "name" }) 
 public class Employee {  
  @XmlElement(nillable = true)  
  protected List addresses;  
  protected int age;  
  protected String name;
 }

and strangely enough there is no setter method for the list of addresses. There is a clear explanation for the same in the comments above the getter for the addresses property as:
This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make to the returned list will be present inside the JAXB object. This is why there is not a <CODE>set</CODE> method for the addresses property.
Solution: But this doesn't mean that this is a dead end. As mentioned in the original blog, you can replace the generated Employee class with the one that you used in the Web Service. With that you shall have the setter method for addresses list and the client invokes the Web Service perfectly.

Monday, September 15, 2008

Exposing JAX-WS web service using Spring

We start by creating a web project in Eclipse. I used Sysdeo Tomcat plugin.This will create a basic web application structure. Note that this does not create a web.xml file in the WEB-INF folder.
Add the following jar files to the WEB-INF\lib folder

activation.jar
commons-collections.jar
commons-logging.jar
FastInfoset.jar
http.jar
jaxb-api.jar
jaxb-impl.jar
jaxb-xjc.jar
jaxws-api.jar
jaxws-rt.jar
jaxws-spring-1.7.jar
jsr173_api.jar
jsr181-api.jar
jsr250-api.jar
resolver.jar
saaj-api.jar
saaj-impl.jar
sjsxp.jar
spring.jar
stax-ex.jar
streambuffer.jar
xbean-spring-2.8.jar

You are required to download the jaxws-spring-1.X.jar from https://maven2-repository.dev.java.net/source/browse/*checkout*/maven2- repository/trunk/www/repository/org/jvnet/jax-ws-commons/spring/jaxws-spring/1.8/jaxws-spring-1.8.jar?rev=3913 (This is the latest available version. I used 1.7 version.)
If you are using Java SE 6 some of the jars may not be required. Also if you need to use the latest version of JAX-WS with Java SE 6 you will need to use the endorsed mechanism for loading the JAX-WS libraries.I am using Java SE 5. Here is the simple web service class:

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;
import javax.jws.soap.SOAPBinding.Use;

/**
* Please run the wsgen if you are using Style.DOCUMENT
* Style.RPC used only for demo purpose.
* @author
*
*/
@WebService
@SOAPBinding(style = Style.RPC, use = Use.LITERAL)
public class HelloService {

   @WebMethod
   public String sayHello(@WebParam(name="name")String name) {
       return "Hello " + name;
   }

}
Create an applicationContext.xml file in the WEB-INF folder as shown.


   
   

   

Create a web.xml file in the WEB-INF folder and add the following entries to it.

    contextConfigLocation
    /WEB-INF/applicationContext.xml




   
       org.springframework.web.context.ContextLoaderListener
   




   jaxws-servlet
   
      com.sun.xml.ws.transport.http.servlet.WSSpringServlet
   



   jaxws-servlet
   /hello



This service is configured using the <wss:binding> tag in the applicationContext.xml file as described above. <wss:binding> definitions define what services are exposed to which part of the URL space. You can also use the nested syntax for the configuration as shown below:
 
    
        
    

Server side Handlers for the service can also be configured using the <ws:service> definition as shown below:




If the service uses more than one handler, handlers can be nested.

   
       
       
   

Deploy the web project on tomcat (tested on Tomcat 6.0). The service WSDL can be accessed using the following URL http://<your-ip>:<tomcat-port>/<project-name>/hello?wsdl
Also I would suggest to have a look at Apache CXF.

Thursday, September 11, 2008

Using annotated classes as web method params or return types

Some time back one of my friends was writing a web service in which certain web methods either returned or received Entity classes as parameters. When ever the wsgen command was fired an error as below was generated:
C:\eclipse\workspace\TopLinkExample\bin>wsgen -cp . service.TopLinkImpl .\org\acme\entity\Order.class: warning: Cannot find annotation method 'name()' i n type 'javax.persistence.Table': class file for javax.persistence.Table not fou nd .\org\acme\entity\Order.class: warning: Cannot find annotation method 'strategy( )' in type 'javax.persistence.GeneratedValue': class file for javax.persistence. GeneratedValue not found Problem encountered during annotation processing; see stacktrace below for more information. java.lang.NullPointerException at com.sun.tools.internal.ws.processor.modeler.annotation.WebServiceAP.i sSubtype(WebServiceAP.java:418) at...
After pondering for a while we figured out that the required classes (persistence related jars) were not in the class path.
So the following command got our stuff working:
C:\eclipse\workspace\TopLinkExample\bin>set CLASSPATH=%CLASSPATH%;C:\eclipse\wor kspace\TopLinkExample\lib\ejb3-persistence.jar;C:\eclipse\workspace\TopLinkExamp le\lib\jboss-ejb3x.jar;C:\eclipse\workspace\TopLinkExample\lib\toplink-essential s.jar;.; C:\eclipse\workspace\TopLinkExample\bin>wsgen -cp %CLASSPATH%;. service.TopLinkImpl
Even though this seems to be a very simple problem, during the time we were searching for the solution, we found that most of the people had this problem and didn't know the solution, whereas the -cp . in the wsgen command was going unnoticed.

It is not necessary to use wsimport generated POJO Files

When we have a Web Service in which we either pass the POJO object as a parameter or as a return type, by running wsimport command in order to generate Web Service Client similiar POJO classes are generated.
Suppose you have a Web Service with a Web Method that takes and returns a POJO of Class Employee:-
public class Employee {
 private int age;
 private String name;

 public Employee() {
 }

 public Employee(int age, String name) {
  super();
  this.age = age;
  this.name = name;
 }
 
 //write getter setter for properties to comply to Java Bean/POJO standard
}
and the Web Method is:
@WebMethod
 public Employee printEmployee(Employee e) {
  System.out.println(e.getName() + " - " + e.getAge());
  return e;
 }
On generating the client, an Employee POJO is automatically generated but with some XML stuff and no constructor i.e. default constructor:
@XmlAccessorType(XmlAccessType.FIELD)  
 @XmlType(name = "employee", propOrder = {"age", "name"})  
 public class Employee {  
  protected int age;  
  protected String name;  
  // Setters and Getters for the properties are also generated 
 }
There seems to be two things about this generated POJO that irritates me:
  1. The newly generated Employee POJO has a lot of XML stuff that does not do any work in service invocation.
  2. I miss the constructor that takes in values for the POJO i.e. new Employee("name", 23);
Solution is simple:- Delete the generated POJO's from the client generated by wsimport command and copy the POJO's from the Web Service side to the generated client code. The package name of the POJO need not be the same on the Web Service and the Client side i.e. on the Web Service it can be pack.Employee where as in client it can be client.Employee. It works without any problems.

Saturday, April 26, 2008

Basic Security on JAXWS 2.0

You can apply basic realm security to your webservice. In the web.xml, you have to paste certain tags given below. Don’t apply the Basic Realm for GET. The client code makes a GET request to get the port. Apply Basic Realm for POST. The client code will put the authentication values in the headers as:
 Calculator calculator = (new CalculatorService()).getCalculatorPort(); 
 BindingProvider provider = (BindingProvider) calculator; 
 provider.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "navnit"); 
 provider.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "leo");
The web.xml section is as below:

 
  Protected Site
  
  /*
  
  DELETE
  POST
  PUT
 
 
 
  
  CalculatorUser
 




 BASIC
 Example Basic Authentication




 Test role
 CalculatorUser

You also need to create a role in the tomcat-users.xml and a user/password.


Wednesday, January 16, 2008

Using JAXWS 2.1 on Java 6.0

To use JAX-WS 2.1 on Java 6.0 and Tomcat 6.0, place all the libraries in the JAXWS 2.1 in “<TOMCAT_HOME>\endorsed” directory. To run the client, you have to have the JAX-WS 2.1 library (jaxb-api.jar) in your bootstrap. For this put the jaxb-api.jar from the JAX-WS 2.1 distribution in the "<JAVA_HOME>/jre/lib/"endorsed.