Monthly Archives: May 2012

JAX-WS on Glassfish using wsgen

Several times of attempt on creating a JAXWS WebService with CXF, Glassfish, and Eclipse combination, I concluded that there are some issues on the development environment (CXF 2.6.0, Glassfish 3.1.2, and Eclipse Indigo). I followed several tutorials, but I always encountered either NPE or missing WSDL and failed to deploy EAR message.

CXF site suggested that Glassfish Metro library will overwrite CXF, so disable class-load in sun-web.xml page, which I did. Glassfish issue log suggested that generated wsdl is not placed under WEB-INF folder. Wizard generated a wsdl file directly under WebContent. When I moved wsdl folder under WEB-INF and modified wsdl location, I still had the same error.

Now, forget the Wizard in the Eclipse. I created JAX-WS WebService using wsgen. First, create a minimum Dynamic Web Project and create a WebService implementation class with the annotations.

@WebService
public class ExampleService {

@WebMethod
public int add( int a, int b)
{
return a + b;
}

@WebMethod
public String concat(String first, String second)
{
return first + second;
}
}

Now, run wsgen command.

msjo@local $ wsgen -verbose -classpath build/classes/ -wsdl -r WebContent/WEB-INF/wsdl -s src -d build
/classes/ com.mjtoolbox.ws.ExampleService

warning: The apt tool and its associated API are planned to be
removed in the next major JDK release.  These features have been
superseded by javac and the standardized annotation processing API,
javax.annotation.processing and javax.lang.model.  Users are
recommended to migrate to the annotation processing features of
javac; see the javac man page for more information.
Note:   ap round: 1
[ProcessedMethods Class: com.mjtoolbox.ws.ExampleService]
[should process method: add hasWebMethods: true ]
[endpointReferencesInterface: false]
[declaring class has WebSevice: true]
[returning: true]
[WrapperGen - method: add(int,int)]
[method.getDeclaringType(): com.mjtoolbox.ws.ExampleService]
[requestWrapper: com.mjtoolbox.ws.jaxws.Add]
[should process method: concat hasWebMethods: true ]
[endpointReferencesInterface: false]
[declaring class has WebSevice: true]
[returning: true]
[WrapperGen - method: concat(java.lang.String,java.lang.String)]
[method.getDeclaringType(): com.mjtoolbox.ws.ExampleService]
[requestWrapper: com.mjtoolbox.ws.jaxws.Concat]
[ProcessedMethods Class: java.lang.Object]
com\mjtoolbox\ws\jaxws\Add.java
com\mjtoolbox\ws\jaxws\AddResponse.java
com\mjtoolbox\ws\jaxws\Concat.java
com\mjtoolbox\ws\jaxws\ConcatResponse.java
Note:   ap round: 2

In order to run it properly, I have to build the project in the Eclipse. It seems Eclipse doesn’t build the project until Run on Server is executed. Have a look at the wsdl file and update the “REPLACE_WITH_ACTUAL_URL” with the actual WebService Endpoint. Make sure to replace URL with actual Service name defined in the wsdl like below.

<service name="ExampleServiceService">
<port name="ExampleServicePort"
binding="tns:ExampleServicePortBinding">
<soap:address
location="http://127.0.0.1:8080/WSbare/ExampleServiceService"/>
</port>
</service>

Another way of testing JAX-WS WebService is using Endpoint API.

Endpoint API provide the light-weight HTTP container that is included in Java SE 6. Without the web server, we can deploy the same WebService to the desire location. Of course, wsdl will be generated automatically. URL can be anything after the port number.

 

public static void main(String[] args){
Endpoint.publish("http://hy149411:8080/WSbare/Examplews", new ExampleService());
}

JAX-RS parsing XML

I have encountered a few issues when I try to parse XML object from the JAX-RS WebService. Many tutorials out there uses simple object. Just like I did in the previous post. Using one single class. However, in reality, if the Java class represents a complex object, typical Java will implements interface(s) and involves in parent/child relationship. In this circumstance, it is difficult to put @XmlRootElement all over the classes.

There are three options that we can take.

1. @XmlJavaTypeAdapter : Apply the adapter to hide implementation. By using adapter, you can specify the object that will be marshaled/unmarsharled. See JAXB Guide. I tried myself, but it has a problem at marshaling and I didn’t have a chance to dive into this route. At least, it didn’t complain about the object is an interface.

2. Simplify the object : Use a transfer class to make the object simple. Unless you need to pass the whole object & its attributes, simply the structure to represent the data.

3. Assign @XmlRootElement in the hierarchy : Make the JAXB work all the object in the hierarchy. Still it may require a trick. In case, declared variable is an Interface type, if the Interface is not complex, use substitute variable. If complex, use #1 technique or declare @XmlRootElement tag in the implementation class.

Conclusion

In my opinion, #2 seems the best solution when you need to deal with a complex object. Multiple inheritances with interface and abstract classes. Class is structured in this way to be flexible and useful within the domain. However, it may not be suitable to the outside of the domain. WebService is the tool to communicate the data to the outside, so why not spending little more time to make the object simpler?

JAX-RS on Weblogic using Jersey

In my current project, I have a RESTful WebService. Because we are using Weblogic 10.3 with JEE5, I have to use Jersey. Due to the requirement, I used XML instead of JSON. Also, I’d like to focus on JAX-RS client side using Jersey. Server side is relatively simple and lots of tutorials are available.

  • Weblogic 10.3
  • IntelliJ
  • Jersey 1.12
  • Xerces 2.11

[XML binding with POJO – using JAXBElement]

Message.java

public class Message
{
//Asset, Related ID, Functional Area, Message Type, Received At
public String asset;
public String relatedID;
public String functionalArea;
public String messageType;
public String receivedAt;

@Override
public String toString()
{
return "Message{" +
"asset='" + asset + '\'' +
", relatedID='" + relatedID + '\'' +
", functionalArea='" + functionalArea + '\'' +
", messageType='" + messageType + '\'' +
", receivedAt='" + receivedAt + '\'' +
'}';
}
}

Simple object without any complex type. Easy to parse.

@Path("/nprf")
public class NprfResource......(obmitted)...

  @GET
  @Path("/testXML")
  @Produces(MediaType.APPLICATION_XML)
  public JAXBElement<Message> testXML()
  {
    Message message = new Message();
    message.asset = "test";
    message.functionalArea = "myarea";
    message.messageType = "testtype";
    message.receivedAt = "January 1";
    message.relatedID = "000";

    return new JAXBElement( new QName( "testXML" ), Message.class, message );
  }

Defining return type as JAXBElement. It will be useful if the returned object type is complex object and don’t want to add XmlRootElement tag all over.

 try
    {
      // RESTful WebService Client call. Optional to create a DefaultClientConfig.
      DefaultClientConfig config = new DefaultClientConfig();
      config.getProperties().put( ClientConfig.FEATURE_DISABLE_XML_SECURITY, 
       "true" );
      Client client = Client.create( config );
      WebResource webResource 
       = client.resource( "http://localhost:30020/CMWeb/rest/nprf" );

      GenericType<JAXBElement> messageType = new GenericType<JAXBElement>()
      {
      };
      Message message = webResource.path( "testXML" )
                        .accept( MediaType.APPLICATION_XML_TYPE )
                        .get( messageType ).getValue();
      System.out.println( "### " + message );

    }
    catch ( Exception e )
    {
      log.safeError( "testTestXML", e.getClass().getName(), e );
      fail( e.getMessage() );
    }

I was getting “org.xml.sax.SAXNotRecognizedException: http://javax.xml.XMLConstants/feature/secure-processing&#8221; exception when I tried to parse XML to Java object. To resolve the issue, I had to replace Xerces with newer version. I think somewhere in the environment uses the old version of Xerces.

From URL result

  <?xml version=”1.0″ encoding=”UTF-8″ standalone=”yes” ?>
<testXML>
  <asset>test</asset>
  <relatedID>000</relatedID>
  <functionalArea>myarea</functionalArea>
  <messageType>testtype</messageType>
  <receivedAt>January 1</receivedAt>
  </testXML>

Test Result below.

About to run testTestXML
-----------------------------------------------------------------
### Message{asset='test', 
relatedID='000', 
functionalArea='myarea', 
messageType='testtype', 
receivedAt='January 1'}