Wednesday, December 3, 2008

GuiceManaged and webservice conflict

If your a user with Guice and JAX-WS, there is a conflict betwin resource injection.

JAX-WS support @Resource annotation, so that service can access context object like "WebServiceContext". If you use Guice to inject some other resource to same Service jax-ws injection didn't work.

Not relay sure about its Guice issue or JAX-WS issue.

This bug killed me more than 4 hrs. Hope it help others

Friday, November 7, 2008

Creating Codec Specific Custom Documentation

In my last post i was described about how to create JAX-WS custom codec. In this post I am going to explain how to create endpoint documentation for your codec.

Your codec implements "com.sun.xml.ws.api.server.EndpointComponent", So your codec act as one endpoint, every endpoint component having its own SPI. So you can create your SPI handler.

While creating codec "getSPI" method was unexplained. Here it come one of the use of it.


public @Nullable T getSPI(@NotNull Class type) {

if (type == HttpMetadataPublisher.class) {
if (metadataPublisher == null)
metadataPublisher = new HttpMetadataPublisher(){
@Override
public boolean handleMetadataRequest(HttpAdapter arg0,
WSHTTPConnection arg1) throws IOException {
return false;
}

};
return type.cast(metadataPublisher);
}
return null;
}


When ever get request made to the point your codec "getSPI" method called. Normaly it returns HttpMetadataPublisher object. Then your publishers "handleMetadataRequest" method called by request handler. inside "handleMetadataRequest" you can write your custom documentation and return "true".

In case if you return "false" JAX-WS default end point documentation created.


Following steps help you to create simple custom documentation for endpoint.

Step 1: Create a class "XXXHttpMetadataPublisher" which extends from com.sun.xml.ws.transport.http.HttpMetadataPublisher.


Step 2: Overwrite a method "public boolean handleMetadataRequest(HttpAdapter adapter,
WSHTTPConnection connection)"

Step 3: Inside method parse a query string from "WSHTTPConnection".

String queryString = con.getQueryString()


Step 4: based on query string write your html help document into "WSHTTPConnection" outputStream. And return "true"

Now your "XXXHttpMetadataPublisher" looks like

import java.io.IOException;

import com.sun.xml.ws.transport.http.HttpAdapter;
import com.sun.xml.ws.transport.http.HttpMetadataPublisher;
import com.sun.xml.ws.transport.http.WSHTTPConnection;

public class XXXHttpMetadataPublisher extends HttpMetadataPublisher {

@Override
public boolean handleMetadataRequest(HttpAdapter adapter,
WSHTTPConnection connection) throws IOException {
String queryString = connection.getQueryString();
if (queryString.equals("hello") ){
connection.getOutput().write("I Have to write document here. Templete engines may help me here....".getBytes());
return true;
}
return false;
}

}


Step 5: Now go to "getSPI" method in your XXXCodec class, check type is "HttpMetadataPublisher", if its true then return your "XXXHttpMetadataPublisher".

your codec getSPI method looks like following

public @Nullable T getSPI(@NotNull Class type) {
if (type == HttpMetadataPublisher.class) {
if (metadataPublisher == null)
metadataPublisher = new XXXHttpMetadataPublisher();
return type.cast(metadataPublisher);
}
return null;
}


Your Done!!!


Now if you browse your end point with "hello" as query string, you get the the "hello" specific document.






Tips

You may get following type of exception while developing

SEVERE: WSSERVLET11: failed to parse runtime descriptor: javax.xml.ws.WebServiceException: Wrong binding ID: http://jsonplugin.googlecode.com/json/
javax.xml.ws.WebServiceException: Wrong binding ID: http://jsonplugin.googlecode.com/json/
at com.sun.xml.ws.api.BindingID.parse(BindingID.java:260)
at com.sun.xml.ws.transport.http.DeploymentDescriptorParser.createBinding(DeploymentDescriptorParser.java:295)
at com.sun.xml.ws.transport.http.DeploymentDescriptorParser.parseAdapters(DeploymentDescriptorParser.java:243)
at com.sun.xml.ws.transport.http.DeploymentDescriptorParser.parse(DeploymentDescriptorParser.java:147)
at com.sun.xml.ws.transport.http.servlet.WSServletContextListener.contextInitialized(WSServletContextListener.java:108)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3843)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4350)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:719)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
at org.apache.catalina.core.StandardService.start(StandardService.java:516)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:710)
at org.apache.catalina.startup.Catalina.start(Catalina.java:578)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:288)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:413)


It means your "com.sun.xml.ws.api.BindingIDFactory" file located inside META-INF/services not found.

You may forgot to place your jar file inside your WEB-INF/lib or your file location or name is not correct.

Saturday, November 1, 2008

10 steps to create JAX-WS Codec

Sun JAX-WS is the webservice implementation from sun. While studying for my Sun Webservice certification as well in my day to day work I do use Sun JAX-WS implementation. Compare to other webservice implementation JAX-WS performs better as well by default included with Java 1.6 version.

Webservice's normaly use SOAP-XML communication (http tuneling etc). But it's not fully enough now days. For example JSON webservice communication is more demanding. Using json web-service, Web-browsers can directly communicate with endpoints. Same like depends on application the way we like to communcate with webservice end point differ, following is short list of possible communication format.

  1. SOAP-XML.
  2. JSON.
  3. SMTP
  4. SQL.
To make any format of communication we need coder/decoder (Codec). so that it can convert the input/output as application readable or required format.

In this blog, I am going to explain how to write custom Codec for JAX-WS implementation in 10 steps.

Step 1: Create Project structure.

I do use eclipse with webtools as IDE. Create new J2ee utility project.



Eclipse create source and META-INF folder for you. After creating project add JAX-WS ri jars (webservices-api.jar,webservices-rt-1.3-SNAPSHOT.jar,webservices-tools.jar)into your project classpath. You can get this jars as part of java.net metro Metro



Step 2: Create XXXContentType.

First define communincation Content-Type. For example json its "application/json".

XXXContentType class implements "com.sun.xml.ws.api.pipe.ContentType" interface.

ContentType inteface contains 3 methods getAcceptHeader,
getContentType, and getSOAPActionHeader. All are self explained.



Step 3: Create XXXCodec

This is a main programing part of writing custom codec. You have to write serialization and deserialisation of java objects in this step.

Create a XXXCodec class implements from EndpointAwareCodec, EndpointComponent. methods implements from interface are self explanatory.

You have to write your decode and encode logic in respective methods.

While decoding read input data from InputStream and create "com.sun.xml.ws.api.message.Message" object and set it in package. Same like while encoding serialize java object into XXX content format and write in output stream.


package com.jaxws.codec;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import com.sun.xml.ws.api.SOAPVersion;
import com.sun.xml.ws.api.WSBinding;
import com.sun.xml.ws.api.message.Message;
import com.sun.xml.ws.api.message.Messages;
import com.sun.xml.ws.api.message.Packet;
import com.sun.xml.ws.api.pipe.Codec;
import com.sun.xml.ws.api.pipe.ContentType;
import com.sun.xml.ws.api.server.EndpointAwareCodec;
import com.sun.xml.ws.api.server.EndpointComponent;
import com.sun.xml.ws.api.server.WSEndpoint;
public class XXXCodec implements EndpointAwareCodec, EndpointComponent {
private static final ContentType xxxContentType = new XXXContentType();
private final WSBinding binding;
private WSEndpoint endpoint;

public XXXCodec(XXXCodec copy) {
this(copy.binding);
this.endpoint = copy.endpoint;
}
public XXXCodec(WSBinding binding) {
this.binding = binding;
}
@Override
public void setEndpoint(WSEndpoint endpoint) {
this.endpoint = endpoint;
endpoint.getComponentRegistry().add(this);
}
@Override
public Codec copy() {
return new XXXCodec(this);
}
@Override
public void decode(InputStream input, String contentType, Packet response)
throws IOException {
Message message = Messages.createEmpty(SOAPVersion.SOAP_11);
// TODO write logic to read input
stream and create Message object and set it in
packet.
response.setMessage(message);
}
@Override
public void decode(ReadableByteChannel arg0, String arg1, Packet arg2) {
throw new
UnsupportedOperationException();
}
@Override
public ContentType encode(Packet packet, OutputStream out)throws IOException {
Message message = packet.getMessage();
if (message != null) {
// TODO write logic to serialize message.
out.write("My XXX Decoder not implement yet".getBytes());
}
return xxxContentType;
}
@Override
public ContentType encode(Packet arg0, WritableByteChannel arg1) {
throw new UnsupportedOperationException();
}
@Override
public String getMimeType() {
return XXXContentType.XXX_CONTENT_TYPE;
}
@Override
public ContentType getStaticContentType(Packet arg0) {
return xxxContentType;
}
@Override
public T getSPI(Class arg0) {
// TODO Auto-generated method stub
return null;
}
}


Step 4: Create XXXBindingID.

Now its time to define ID for your codec. This id is used while defining endpoint. This ID can be any string. Normaly its URI. like http://www.mycompany.com/codec/xxx/

Create a class XXXBindingID extends com.sun.xml.ws.api.BindingID, and overwrite/implement methods.




Step 5: Create BindingIDFactory.

Add the defined Id to the factory pattern. So that it can serve your codec using its ID.


Your factory class extends "com.sun.xml.ws.api.BindingIDFactory".
Create your XXXBindingIDFactory class that extends form "com.sun.xml.ws.api.BindingIDFactory"


Step 6: Create services folder and BindingIDFactoy id file.

Now its time to pack XXX codec.


JAX-WS uses all bindingID factory declared in file "com.sun.xml.ws.api.BindingIDFactory" located inside "META-INF/services" folder.

Create "services" folder inside META-INF and create a file "com.sun.xml.ws.api.BindingIDFactory" (all dots are also part of file name), inside a file declare your XXXBindingIDFactory class.





Step 7: Create JAR.

Now select src folder and export as jar. Optionaly your may write ant script or other tools to create jar. Make sure META-INF folder included in jar with services folder.

After creating jar your custom Codec ready to use.

Step 8: Create testing WebService implementation class.

Now to test your Codec , create new Webproject and place the Codec jar inside WEB-INF/lib. and create your service implementation class.

For more about creating this test project for JAX-WS look at samples provided with Webservice developers pack. Or look in to attached sources.

Step 9: Create entry in sun-jaxws.xml.

define jax-ws endpoint with binding id.



<endpoint
name="XXXCodecTest"
implementation="com.test.codec.TestServiceImpl"
url-pattern="/xxx/ping"
binding="http://www.mycompany.com/codec/xxx/"/>
</endpoints>

Step 10: Test
Use your favorite tool and send post data with content type as "application/XXX"

Endpoint URL : http://localhost:8080/CustomCodecTest/xxx/ping






http://pluginsdemo.googlepages.com/CustomCodec.zip

(Note: this zip didn't include webservices-rt-1.3-SNAPSHOT.jar due to size, you can download and include it in your project)

-Sundar




-sundaramurthis@gmail.com