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.

Monday, September 8, 2008

Setting network b/w Linux machines using USB cable

To setup the USB network between two linux machines perform the following steps :- 1) Attach both the Linux machines using a USB cable. 2) Open the terminal window on each of the machines and type the following command (On first machine) ]# ifconfig usb0 10.100.10.101 netmask 255.255.255.0 (On second machine) ]# ifconfig usb0 10.100.10.102 netmask 255.255.255.0 Once done, try to ping the other machine using the assigned ip i.e. 10.100.10.101. This technique has been tested with two machines with Fedora 9 on one machine and busybox on other. It should be able to work on other combinations of linux too.

Sunday, September 7, 2008

Java Socket client issue on Linux

In the case that the socket client is on the windows and server on linux & the server goes down, the client on windows identified immediately and gives an exception. For a scenario vice versa i.e. socket client on linux and server on any other machine (currently tested on windows), the client waits for too long for a timeout. To deal this situation instead of opening a socket client like:
 Socket socket = new Socket(“10.1.25.186”, 8080);
You should use:
InetAddress inetAddr = InetAddress.getByName(“navnit.tempo.local”);  
SocketAddress socAddr = new InetSocketAddress(inetAddr, 8080);  
Socket socket = new Socket();  socket.connect(socAddr, 5000); // 5000 is the timeout in millis.

Extracting text from documents using Java

Extracting the text from the HTM/HTML document i.e. pulling all the text except the HTML tags is done as:
URL url = new URL("http://localhost:8080/index.jsp");  
EditorKit kit = new HTMLEditorKit();  
Document document = kit.createDefaultDocument();  
kit.read(url.openStream(), document, 0);  
System.out.println(document.getText(0, document.getLength()));
For PDF text extraction use pdfbox from www.pdfbox.org
URL url = new URL("http://localhost:8080/Document.pdf");  
PDDocument document = PDDocument.load(url.openStream());  
PDFTextStripper pdfStripper = new PDFTextStripper();  pdfStripper.setSortByPosition(false);  
pdfStripper.setStartPage(1); //from which page to start  
pdfStripper.setEndPage(3);  //on which page to end
System.out.println(pdfStripper.getText(document));  
document.close();
For MS Office Suite documents text extraction use Apache POI ( http://poi.apache.org/ ) Sample code for extracting text from a .doc file is as follows:-
POIFSFileSystem doc = new POIFSFileSystem(new FileInputStream("c:/Resume.doc"));
WordExtractor extractor = new WordExtractor( doc );
System.out.println(extractor.getText());
A good article on the same can be found at http://www.informit.com/guides/content.aspx?g=java&seqNum=354

Precision subtraction of floats

The normal subtraction of the double values in java result in a very long answer e.g. System.out.println(0.123 - 0.100); will result in 0.022999999999999993 as opposed to the expected 0.023. The Math.round function won't work here as it Returns the closest long/int to the argument.This precision problem can be fixed using the BigDecimal class.
BigDecimal first = new BigDecimal(0.123);
BigDecimal second = new BigDecimal(0.100);
System.out.println(first.subtract(second, new MathContext(2)));
The above program rounds off the answer to a precision of 2. i.e. the result will be 0.023.

Socket connection via proxy

To make the hits to internet programmatically via a proxy, you need to launch the program with the following JVM arguments. The program ConnectionTest contains the code to connect to some server on the internet and if in order to do so it needs to go via a proxy then run it as follows.
 
>java -Dhttp.proxyHost=proxy2.temp.org -Dhttp.proxyPort=8888 pack.ConnectionTest

Running the above code without the JVM arguments, it won't connect.

Generating same hashcode for similiar objects

Always override hashCode method if you are overriding the equals method of a java class. A POJO containing the same values in the properties can generate different hashCodes by default. If you need a way to generate the same hashCodes for POJO’s with same properties, instead of writing your own logic like adding properties or any other method, consider using apache’s commons-lang class HashCodeBuilder. This is specially worth considering if the objects are being added to data structures that use hashing mechanism for storing objects e.g. HashSet, HashMap, etc.

@Override  
public int hashCode() {
    return HashCodeBuilder.reflectionHashCode(this);
}