Labels

Sunday, May 6, 2012

TinyWebServer.java an embedded sun httpserver implementation since JRE 1.6 less than 300 lines!

TinyWebServer.java is a Java implementation based on the embedded Sun httpserver which available since JRE 1.6. (in rt.jar) and is less than 300 lines of codes.

During my development work, I started out using the JETTY a small and lightweight web engine, but I need to attach a few jars to my projects. My automated build/deployment also need to pack these libraries as well since my test cases depend on it.

Since version 1.6 of the sun JRE, the rt.jar come with the embedded version (limited functionality) that work well, and just what I needed to set up my stub web services to ensure reliable test run. I also use this "TinyWebServer.java" for a small demo to my student as well. So, here are the codes for it. Next post I will share with you on how I use it in my JUnit test to set up and run my stub web services.

Remember, if you get errors in your eclipse project, simply remove the "System Library" and add it back, this will remove the security policy from the eclipse build.

package org.cnci.util;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URL;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.xml.ws.Endpoint;
import org.apache.log4j.Logger;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsServer;
/**
 * TinyWebServer
 * 
 * @author T.A. Nguyen
 * 
 *  Tiny Embedded (Stub) Web Server, intended for testing only.  This implementation
 *  will take advantage of the "sun" httpserver package that shipped with the JRE 
 *  since release 1.6.
 *  
 *  Provide an easy creation of a TinyWebServer that can be start up and host as
 *  an embedded (secure) web server.  Just pass in the URL as you intended to access
 *  and it will attempt to host in that url, whether it is secure or not.  This 
 *  will make it an ideal to configure and setup as a unit test stub server.
 * 
 *  By default, Eclipse will add restriction to com.sun.net.httpserver.*
 *  package access, just need to remove the JRE library and add it back
 *  then it will allow you to use it.
 * 
 *  Usage: 
 *  
 *  TinyWebServer server = new TinyWebServer("https://*:8080"); 
 *  AddressBookSoap myBook = new AddressBookSoapImpl(); // your service implementation here 
 *  server.addSoapService("/ws/myBook", myBook);
 *  PersonnelSoap myHR = new StubPersonnelSoapImpl(); // my other web service, local stub 
 *  server.addSoapService("/ws/myHR", myHR); 
 *  server.start();
 *  
 */
public class TinyWebServer {
  private static final String SSL_VERSION = "SSLv3";
  private static Logger log = Logger.getLogger(TinyWebServer.class);
  private static final String KEYSTORE_FILENAME = "localhost.server.keystore.jks";
  private static final String KEYSTORE_PASSWORD = "cnci.org";
  private static final int RETRY_TIME = 5;
  private static final int SLEEP_TIME = 500;
  private static final String DEFAULT_PROTOCOL = "https";
  private static final String DEFAULT_HOST = "localhost";
  private static final int DEFAULT_PORT = 8080;
  private HttpServer server = null;
  private Map services = new HashMap();
  private List endpoints = new ArrayList();
  private String url;      // same as the client access url
  private String protocol; // http/https
  private String host;     // ip or host name
  private int port;
  /**
   * Simple, just pass the url as the client expected, 
   * make sure that the host is of a local host name or ip 
   * otherwise it will default to "localhost"
   * 
   * https://*:8080 will listen to all local addresses on port 8080 via https (secure) protocol
   * https://localhost:8080 will listen to 127.0.0.1:8080 via https (secure) protocol
   * http://*:8080 will listen to all local addresses on port 8080 via http protocol 
   * 
   * @param host
   * @param port
   */
  public TinyWebServer(String url) {
    this.url = url;
  }
   
  public TinyWebServer(String host, int port) {
    this.host = host;
    this.port = port;
  }
  public void addSoapService(String context, Object service) throws Exception {
    if (context == null || context.trim().length() <= 0) {
      throw new Exception("Context uri is a required parameter.");
    }
    if (service == null) {
      throw new Exception("Soap service handler is a required parameter.");
    }
    if (services.containsKey(context)) {
      throw new Exception("Context uri is already exist.");
    }
    services.put(context, service);
    createEndpoint(getServer().createContext(context), service);
  }
  public void addHttpService(String context, HttpHandler handler, Executor executor) throws Exception {
    if (context == null || context.trim().length() <= 0) {
      throw new Exception("Context uri is a required parameter.");
    }
    if (handler == null) {
      throw new Exception("HttpHandler service handler is a required parameter.");
    }
    if (services.containsKey(context)) {
      throw new Exception("Context uri is already exist.");
    }
    services.put(context, handler);
    server.createContext(context, handler);
    if (executor == null) executor = Executors.newCachedThreadPool();
    // Executors.newFixedThreadPool(5)
    server.setExecutor(executor); // null for Default executor
  }
  public void start() throws Exception {
    HttpServer s = getServer();
    Exception e = null;
    boolean started = false;
    int count = 0;
    while (!started && count  0) {
          port = u.getPort();
        }
        if (u.getProtocol() != null) {
          protocol = u.getProtocol();
        }
      } catch (Exception e) {
        // ignore
      }
    }
    if (protocol == null) protocol = DEFAULT_PROTOCOL;
    if (host == null) host = DEFAULT_HOST;
    if (port <= 0) port = DEFAULT_PORT;
  }
   
  private boolean isSsl() {
    return DEFAULT_PROTOCOL.equalsIgnoreCase(getProtocol());
  }
   
  private String getHost() {
    if (host == null && url != null) validateUrl();
    return host;
  }
   
  private int getPort() {
    if (host == null && url != null) validateUrl();
    return port;
  }
   
  private String getProtocol() {
    if (host == null && url != null) validateUrl();
    return protocol;
  }
}