Wednesday, December 3, 2008
GuiceManaged and webservice conflict
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
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 @NullableT 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 @NullableT 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
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
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.
- SOAP-XML.
- JSON.
- SMTP
- SQL.
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
publicT 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.
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>
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