JAL-1575 bare bones RestHandler added
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 7 May 2015 15:58:15 +0000 (16:58 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 7 May 2015 15:58:15 +0000 (16:58 +0100)
src/jalview/ext/rbvi/chimera/ChimeraListener.java
src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java
src/jalview/httpserver/AbstractRequestHandler.java
src/jalview/httpserver/HttpServer.java
src/jalview/rest/RestHandler.java [new file with mode: 0644]

index c9ec136..2882cb5 100644 (file)
@@ -6,7 +6,6 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import jalview.httpserver.AbstractRequestHandler;
-import jalview.httpserver.HttpServer;
 import jalview.structure.SelectionSource;
 
 /**
@@ -31,10 +30,19 @@ import jalview.structure.SelectionSource;
 public class ChimeraListener extends AbstractRequestHandler implements
         SelectionSource
 {
+  /*
+   * Chimera notification parameter name
+   */
   private static final String CHIMERA_NOTIFICATION = "chimeraNotification";
 
+  /*
+   * Chimera model changed notifications start with this
+   */
   private static final String MODEL_CHANGED = "ModelChanged: ";
 
+  /*
+   * Chimera selection changed notification message
+   */
   private static final String SELECTION_CHANGED = "SelectionChanged: selection changed\n";
 
   /*
@@ -45,9 +53,9 @@ public class ChimeraListener extends AbstractRequestHandler implements
   private static int chimeraId = 0;
 
   /*
-   * Path below context root that identifies this handler
+   * Prefix for path below context root (myChimeraId is appended)
    */
-  private static final String LISTENER_PATH = "chimera";
+  private static final String PATH_PREFIX = "chimera";
 
   /*
    * Value of chimeraId (0, 1, 2...) for this instance
@@ -59,13 +67,8 @@ public class ChimeraListener extends AbstractRequestHandler implements
    */
   private JalviewChimeraBinding chimeraBinding;
 
-  /*
-   * The URI of this listener
-   */
-  private String uri;
-
   /**
-   * Constructor that also registers this as an Http request handler on path
+   * Constructor that registers this as an Http request handler on path
    * /chimeraN, where N is incremented for each instance. Call getUri to get the
    * resulting URI for this handler.
    * 
@@ -78,18 +81,8 @@ public class ChimeraListener extends AbstractRequestHandler implements
   {
     myChimeraId = chimeraId++;
     this.chimeraBinding = binding;
-    final String path = LISTENER_PATH + myChimeraId;
-    this.uri = HttpServer.getInstance().registerHandler(path, this);
-  }
-
-  /**
-   * Returns the URI on which we are listening
-   * 
-   * @return
-   */
-  public String getUri()
-  {
-    return this.uri;
+    setPath(PATH_PREFIX + myChimeraId);
+    registerHandler();
   }
 
   /**
@@ -126,20 +119,11 @@ public class ChimeraListener extends AbstractRequestHandler implements
   }
 
   /**
-   * Deregister this listener and close it down
-   * 
-   * @throws Exception
+   * Returns a display name for this service
    */
-  public void shutdown()
+  @Override
+  public String getName()
   {
-    try
-    {
-      HttpServer.getInstance().removeHandler(this);
-      stop();
-    } catch (Exception e)
-    {
-      System.err.println("Error stopping chimera listener: "
-              + e.getMessage());
-    }
+    return "ChimeraListener";
   }
 }
index 3c751e7..543cad4 100644 (file)
@@ -40,6 +40,7 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
+import jalview.httpserver.AbstractRequestHandler;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ResidueProperties;
 import jalview.structure.AtomSpec;
@@ -69,7 +70,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
   /*
    * Object which listens to Chimera notifications
    */
-  private ChimeraListener chimeraListener;
+  private AbstractRequestHandler chimeraListener;
 
   /*
    * set if chimera state is being restored from some source - instructs binding
index 45064e7..9642508 100644 (file)
@@ -1,6 +1,7 @@
 package jalview.httpserver;
 
 import java.io.IOException;
+import java.net.BindException;
 import java.util.Collections;
 
 import javax.servlet.ServletException;
@@ -17,6 +18,18 @@ import org.eclipse.jetty.server.handler.AbstractHandler;
  */
 public abstract class AbstractRequestHandler extends AbstractHandler
 {
+
+  /*
+   * The relative path (below context root) of this handler (without /
+   * separators)
+   */
+  private String path;
+
+  /*
+   * The full URI on which this handler listens
+   */
+  private String uri;
+
   /**
    * Handle an incoming Http request.
    */
@@ -88,4 +101,84 @@ public abstract class AbstractRequestHandler extends AbstractHandler
       }
     }
   }
+
+  /**
+   * Returns a display name for the handler
+   * 
+   * @return
+   */
+  public abstract String getName();
+
+  /**
+   * Deregister this listener and close it down
+   * 
+   * @throws Exception
+   */
+  public void shutdown()
+  {
+    try
+    {
+      HttpServer.getInstance().removeHandler(this);
+      stop();
+    } catch (Exception e)
+    {
+      System.err.println("Error stopping " + getName() + ": "
+              + e.getMessage());
+    }
+  }
+
+  /**
+   * Returns the URI on which we are listening
+   * 
+   * @return
+   */
+  public String getUri()
+  {
+    return this.uri;
+  }
+
+  /**
+   * Set the URI to this handler
+   * 
+   * @param u
+   */
+  protected void setUri(String u)
+  {
+    this.uri = u;
+  }
+
+  /**
+   * Sets the relative path to this handler - do this before registering the
+   * handler.
+   * 
+   * @param p
+   */
+  protected void setPath(String p)
+  {
+    this.path = p;
+  }
+
+  /**
+   * Returns the relative path to this handler below the context root (without /
+   * separators)
+   * 
+   * @return
+   */
+  public String getPath()
+  {
+    return this.path;
+  }
+
+  /**
+   * Registers the handler with the HttpServer and reports its URI on stdout
+   * 
+   * @throws BindException
+   *           if no port could be allocated
+   * @throws IllegalStateException
+   *           if this method is called before {@link #setPath}
+   */
+  protected void registerHandler() throws BindException
+  {
+    HttpServer.getInstance().registerHandler(this);
+  }
 }
index e60db9a..fcfefaa 100644 (file)
@@ -16,6 +16,8 @@ import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.handler.HandlerCollection;
 import org.eclipse.jetty.util.thread.QueuedThreadPool;
 
+import jalview.rest.RestHandler;
+
 /**
  * An HttpServer built on Jetty. To use it
  * <ul>
@@ -86,6 +88,11 @@ public class HttpServer
   private HttpServer() throws BindException
   {
     startServer();
+
+    /*
+     * Provides a REST server by default; add more programmatically as required
+     */
+    registerHandler(RestHandler.getInstance());
   }
 
   /**
@@ -200,16 +207,22 @@ public class HttpServer
   }
 
   /**
-   * Register a handler for the given path and returns its URI
+   * Register a handler for the given path and set its URI
    * 
-   * @param path
-   *          a path below the context root (without leading or trailing
-   *          separator)
    * @param handler
    * @return
+   * @throws IllegalStateException
+   *           if handler path has not been set
    */
-  public String registerHandler(String path, AbstractRequestHandler handler)
+  public void registerHandler(AbstractRequestHandler handler)
   {
+    String path = handler.getPath();
+    if (path == null)
+    {
+      throw new IllegalStateException(
+              "Must set handler path before registering handler");
+    }
+
     // http://stackoverflow.com/questions/20043097/jetty-9-embedded-adding-handlers-during-runtime
     ContextHandler ch = new ContextHandler();
     ch.setAllowNullPathInfo(true);
@@ -237,7 +250,9 @@ public class HttpServer
               + e.getMessage());
     }
 
-    return this.contextRoot + ch.getContextPath().substring(1);
+    handler.setUri(this.contextRoot + ch.getContextPath().substring(1));
+    System.out.println("Jalview " + handler.getName()
+            + " handler started on " + handler.getUri());
   }
 
   /**
diff --git a/src/jalview/rest/RestHandler.java b/src/jalview/rest/RestHandler.java
new file mode 100644 (file)
index 0000000..88f23f4
--- /dev/null
@@ -0,0 +1,99 @@
+package jalview.rest;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.BindException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import jalview.httpserver.AbstractRequestHandler;
+
+/**
+ * A simple handler to process (or delegate) HTTP requests on /jalview/rest
+ */
+public class RestHandler extends AbstractRequestHandler
+{
+  private static final String MY_PATH = "rest";
+
+  private static final String MY_NAME = "Rest";
+
+  /**
+   * Singleton instance of this class
+   */
+  private static RestHandler instance = null;
+
+  /**
+   * Returns the singleton instance of this class
+   * 
+   * @return
+   * @throws BindException
+   */
+  public static RestHandler getInstance() throws BindException
+  {
+    synchronized (RestHandler.class)
+    {
+      if (instance == null)
+      {
+        instance = new RestHandler();
+      }
+    }
+    return instance;
+  }
+
+  /**
+   * Private constructor enforces use of singleton
+   * 
+   * @throws BindException
+   */
+  private RestHandler() throws BindException
+  {
+    setPath(MY_PATH);
+
+    /*
+     * We don't register the handler here - this is done as a special case in
+     * HttpServer initialisation; to do it here would invite an infinite loop of
+     * RestHandler/HttpServer constructor
+     */
+  }
+
+  /**
+   * Handle a jalview/rest request
+   * 
+   * @throws IOException
+   */
+  @Override
+  protected void processRequest(HttpServletRequest request,
+          HttpServletResponse response)
+  {
+    /*
+     * Currently just echoes the request; add helper classes as required to
+     * process requests
+     */
+    final String queryString = request.getQueryString();
+    final String reply = "REST not yet implemented; received "
+            + request.getRequestURL()
+            + (queryString == null ? "" : "?" + queryString);
+    System.out.println(reply);
+
+    try
+    {
+      final PrintWriter writer = response.getWriter();
+      writer.write(reply);
+      writer.close();
+    } catch (IOException e)
+    {
+      System.err.println("Error writing REST response: " + e.getMessage());
+    }
+  }
+
+  /**
+   * Returns a display name for this service
+   */
+  @Override
+  public String getName()
+  {
+    return MY_NAME;
+  }
+
+}