Showing posts with label JAX-WS. Show all posts
Showing posts with label JAX-WS. Show all posts

Thursday, October 15, 2009

JAX-WS inside Jetty

Jetty is a nice and easy to use Web Server (see previous post), but it can it handle an open standards, heavy weight JAX-WS Web Service ?
With a bit of luck and glue code, it can.
So first we need a Service Provider Implementation (SPI) from Jetty: J2se6HttpServerSPI
This will make the JAX-WS endpoint use the Jetty server instead its default Sun HttpServer.
To plugin different SPI's you would define this new service in a META-INF/services file, but you can also set a system property, as described here. You can even do this in code which reduces the number of files you need to worry about when refactoring.
So here's an example of a Jetty Server handling a JAX-WS endpoint in combination with a File system directory.
      Server jettyServer = new Server(port);
     HandlerCollection handlerCollection = new HandlerCollection();
     jettyServer.setHandler(handlerCollection);
    
     /** 1) Publish WebService (JettyHttpServerProvider) */ 
     String context = "/web/ws";
             
     // 1.1) register THIS Jetty server with the JettyHttpServerProvider
     new JettyHttpServerProvider().setServer(jettyServer);
      
     // 1.2) make sure JAX-WS endpoint.publish will use our new service provider: JettyHttpServerProvider
     System.setProperty("com.sun.net.httpserver.HttpServerProvider",
           "org.mortbay.jetty.j2se6.JettyHttpServerProvider");
      
     // 1.3) add an empty HandlerCollection to by setup by this provider
     handlerCollection.addHandler( new HandlerCollection() );
      
     // 1.4) use JAX-WS API to publish the endpoint (will use a JettyHttpServerProvider)
     Endpoint endpoint = Endpoint.create(replayServiceImpl);
     endpoint.publish("http://localhost:" + port + "/web/ws", replayServiceImpl);
    
     /** 2) Publish WebGUI (Jetty) */       
     String context = "/gui";

     // 2.1) configure File Resource Handler
     ResourceHandler fileResourceHandler=new ResourceHandler();
     fileResourceHandler.setWelcomeFiles(new String[]{"index.html"});
     fileResourceHandler.setResourceBase(guiPath); // start here
       
     // 2.2) configure 'gui' Context
     ContextHandler guiHandler = new ContextHandler();
     guiHandler.setContextPath(context);
     guiHandler.setResourceBase(".");
     guiHandler.setClassLoader(Thread.currentThread().getContextClassLoader());
     guiHandler.setHandler(fileResourceHandler);
      
     // 2.3) add this context handler
     handlerCollection.addHandler(guiHandler);
 
     /** 3) start JETTY server */
     jettyServer.start();
     jettyServer.join();

Publishing a JAX-WS Endpoint not just to localhost

Interestingly enough, publishing the JAX-WS Endpoint with endpoint.publish(url) as described in a previous post, publishes the WSDL to http://localhost:8080 but this socket is not accessible from any other host ! Strange default, but there you go ...
So in order to really make a JAX-WS Endpoint public to the world, you can revert to the slightly more cumbersome API of com.sun.net.httpserver.HttpServer (at least on Sun's JDK):

HttpServer server = HttpServer.create(new InetSocketAddress(port), 3 ); // backlog
server.start();
  
endpoint.setExecutor( Executors.newFixedThreadPool(10) );
  
// publish WebService
HttpContext wsContext = server.createContext( "/" + service );
  
//wsContext.setAuthenticator(new TestBasicAuthenticator("test"));
endpoint.publish( wsContext );

Monday, September 7, 2009

60 seconds on SOAP-based Web Services

According to this book, there are 74 distinct initiatives trying to define what a Web Service should look like. And I bet, there are at least 74 different abbreviations around to describe different parts of it. So it's surprising how simple it is to create a Web Service with JDK 1.6. First you define an interface with a bunch of annotations:

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

@WebService
// use RPC or 'wrapped' document-style
@SOAPBinding(style = Style.RPC)
//@SOAPBinding(style = Style.DOCUMENT,
// parameterStyle = SOAPBinding.ParameterStyle.WRAPPED )
public interface Example
{
 @WebMethod public String getGreeting( @WebParam(name="myname") String myname);
}
And then implement the service itself. Use a thread-pool if your application is multi-threaded.

import javax.jws.WebService;
import javax.xml.ws.Endpoint;

@WebService(endpointInterface = "simple.Example")
public class ExampleImpl implements Example
{
 @Override
 public String getGreeting(String myname)
 {
   return "hello, " + myname;
 }

 public static void main(String[] args)
 {
   String url = "http://127.0.0.1:9876/example";
   System.out.println("Starting WebService at: " + url);
   System.out.println("WSDL available at:      " + url + "?wsdl");

   Endpoint endpoint = Endpoint.create(new ExampleImpl());
   //endpoint.setExecutor( Executors.newFixedThreadPool(10) );
   endpoint.publish(url);
 }
}
The resulting WSDL definition of this service can be looked at http://127.0.0.1:9876/example?wsdl (use Firefox or IE, not Chrome!). Any SOAP-based client (such as the XML Test Utility in General Interface, see previous post) can now invoke this service.
REST-style would be harder to implement, but easier to test. In the REST case, you can simply do: http://127.0.0.1:9876/rs?name="Pete" for invocations. The difference to the previous solution is that with REST-style, the client uses an HTTP GET to invoke a method and the return value can be any kind of XML, not just a SOAP envelope.
The code above implements the main 'standard', but its also the most bloated possibilty. More efficient, but less standardized, is a message encoding of JSON instead of XML, or better, use the Google Web Toolkit. If you are in full control of the server and the client and don't need to follow standards, GWT seems an excellent choice right now.