c9ec1365ccf0aa77e7a345d1b08bf5170b4dea56
[jalview.git] / src / jalview / ext / rbvi / chimera / ChimeraListener.java
1 package jalview.ext.rbvi.chimera;
2
3 import java.net.BindException;
4
5 import javax.servlet.http.HttpServletRequest;
6 import javax.servlet.http.HttpServletResponse;
7
8 import jalview.httpserver.AbstractRequestHandler;
9 import jalview.httpserver.HttpServer;
10 import jalview.structure.SelectionSource;
11
12 /**
13  * This is a simple Http handler that can listen for selections in Chimera.
14  * <p/>
15  * Lifecycle:
16  * <ul>
17  * <li>Start the Chimera process</li>
18  * <li>Start the REST service on Chimera, get the port number it is listening on
19  * </li>
20  * <li>Start the ChimeraListener, get the URL it is listening on</li>
21  * <li>The first listener started will start the singleton HttpServer</li>
22  * <li>Send a 'listen' command to Chimera with the URL of the listener</li>
23  * <li>When Jalview's Chimera window is closed, shut down the ChimeraListener</li>
24  * <li>Multiple linked Chimera instances will each have a separate listener (but
25  * share one Http server)</li>
26  * </ul>
27  * 
28  * @author gmcarstairs
29  *
30  */
31 public class ChimeraListener extends AbstractRequestHandler implements
32         SelectionSource
33 {
34   private static final String CHIMERA_NOTIFICATION = "chimeraNotification";
35
36   private static final String MODEL_CHANGED = "ModelChanged: ";
37
38   private static final String SELECTION_CHANGED = "SelectionChanged: selection changed\n";
39
40   /*
41    * A static counter so each listener can be associated with a distinct context
42    * root (/chimera0,1,2,3...). This is needed so we can fetch selections from
43    * multiple Chimera instances without confusion.
44    */
45   private static int chimeraId = 0;
46
47   /*
48    * Path below context root that identifies this handler
49    */
50   private static final String LISTENER_PATH = "chimera";
51
52   /*
53    * Value of chimeraId (0, 1, 2...) for this instance
54    */
55   private int myChimeraId = 0;
56
57   /*
58    * A reference to the object by which we can talk to Chimera
59    */
60   private JalviewChimeraBinding chimeraBinding;
61
62   /*
63    * The URI of this listener
64    */
65   private String uri;
66
67   /**
68    * Constructor that also registers this as an Http request handler on path
69    * /chimeraN, where N is incremented for each instance. Call getUri to get the
70    * resulting URI for this handler.
71    * 
72    * @param chimeraBinding
73    * @throws BindException
74    *           if no free port can be assigned
75    */
76   public ChimeraListener(JalviewChimeraBinding binding)
77           throws BindException
78   {
79     myChimeraId = chimeraId++;
80     this.chimeraBinding = binding;
81     final String path = LISTENER_PATH + myChimeraId;
82     this.uri = HttpServer.getInstance().registerHandler(path, this);
83   }
84
85   /**
86    * Returns the URI on which we are listening
87    * 
88    * @return
89    */
90   public String getUri()
91   {
92     return this.uri;
93   }
94
95   /**
96    * Process a message from Chimera
97    */
98   @Override
99   protected void processRequest(HttpServletRequest request,
100           HttpServletResponse response)
101   {
102     // dumpRequest(request);
103     String message = request.getParameter(CHIMERA_NOTIFICATION);
104     if (SELECTION_CHANGED.equals(message))
105     {
106       this.chimeraBinding.highlightChimeraSelection();
107     }
108     else if (message != null && message.startsWith(MODEL_CHANGED))
109     {
110       processModelChanged(message.substring(MODEL_CHANGED.length()));
111     }
112     else
113     {
114       System.err.println("Unexpected chimeraNotification: " + message);
115     }
116   }
117
118   /**
119    * Handler a ModelChanged notification from Chimera
120    * 
121    * @param substring
122    */
123   protected void processModelChanged(String message)
124   {
125     // System.out.println(message + " (not implemented in Jalview)");
126   }
127
128   /**
129    * Deregister this listener and close it down
130    * 
131    * @throws Exception
132    */
133   public void shutdown()
134   {
135     try
136     {
137       HttpServer.getInstance().removeHandler(this);
138       stop();
139     } catch (Exception e)
140     {
141       System.err.println("Error stopping chimera listener: "
142               + e.getMessage());
143     }
144   }
145 }