jabaws is the new jaba!
[jalview.git] / src / jalview / ws / jws2 / Jws2Discoverer.java
1 package jalview.ws.jws2;
2
3 import java.awt.Color;
4 import java.awt.event.ActionEvent;
5 import java.awt.event.ActionListener;
6 import java.beans.PropertyChangeEvent;
7 import java.beans.PropertyChangeListener;
8 import java.io.Closeable;
9 import java.net.ConnectException;
10 import java.net.URL;
11 import java.util.HashSet;
12 import java.util.Hashtable;
13 import java.util.StringTokenizer;
14 import java.util.Vector;
15
16 import javax.swing.JMenu;
17 import javax.swing.JMenuItem;
18
19 import org.apache.log4j.Level;
20
21 import jalview.bin.Cache;
22 import jalview.datamodel.AlignmentView;
23 import jalview.gui.AlignFrame;
24 import jalview.ws.WSMenuEntryProviderI;
25 import compbio.data.msa.MsaWS;
26 import compbio.metadata.Option;
27 import compbio.metadata.Preset;
28 import compbio.metadata.PresetManager;
29 import compbio.metadata.RunnerConfig;
30 import compbio.ws.client.Jws2Client;
31 import compbio.ws.client.Services;
32
33 /**
34  * discoverer for jws2 services. Follows the lightweight service discoverer
35  * pattern (archetyped by EnfinEnvision2OneWay)
36  * 
37  * @author JimP
38  * 
39  */
40 public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
41 {
42   private java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
43           this);
44
45   /**
46    * change listeners are notified of "services" property changes
47    * 
48    * @param listener
49    *          to be added that consumes new services Hashtable object.
50    */
51   public void addPropertyChangeListener(
52           java.beans.PropertyChangeListener listener)
53   {
54     changeSupport.addPropertyChangeListener(listener);
55   }
56
57   /**
58    * 
59    * 
60    * @param listener
61    *          to be removed
62    */
63   public void removePropertyChangeListener(
64           java.beans.PropertyChangeListener listener)
65   {
66     changeSupport.removePropertyChangeListener(listener);
67   }
68
69   boolean running = false;
70
71   Thread oldthread = null;
72
73   @Override
74   public void run()
75   {
76     if (running && oldthread != null && oldthread.isAlive())
77     {
78       return;
79     }
80     running = true;
81     oldthread = Thread.currentThread();
82     try
83     {
84       Class foo = getClass().getClassLoader().loadClass(
85               "compbio.ws.client.Jws2Client");
86     } catch (ClassNotFoundException e)
87     {
88       System.err
89               .println("Not enabling Jalview Webservices version 2: client jar is not available."
90                       + "\nPlease check that your webstart JNLP file is up to date!");
91       running = false;
92       return;
93     }
94     if (services != null)
95     {
96       services.removeAllElements();
97     }
98     for (String jwsservers : getServiceUrls())
99     {
100       try
101       {
102         if (Jws2Client.validURL(jwsservers))
103         {
104           // look for services
105           for (Services srv : Services.values())
106           {
107             MsaWS service = null;
108             try
109             {
110               service = Jws2Client.connect(jwsservers, srv);
111             } catch (Exception e)
112             {
113               System.err.println("Jws2 Discoverer: Problem on "
114                       + jwsservers + " with service " + srv + ":\n"
115                       + e.getMessage());
116               if (!(e instanceof javax.xml.ws.WebServiceException))
117               {
118                 e.printStackTrace();
119               }
120             }
121             ;
122             if (service != null)
123             {
124               addService(jwsservers, srv, service);
125             }
126           }
127
128         }
129         else
130         {
131           Cache.log.info("Ignoring invalid Jws2 service url " + jwsservers);
132         }
133       } catch (Exception e)
134       {
135         e.printStackTrace();
136         Cache.log.warn("Exception when discovering Jws2 services.", e);
137       } catch (Error e)
138       {
139         Cache.log.error("Exception when discovering Jws2 services.", e);
140       }
141     }
142     oldthread = null;
143     running = false;
144     changeSupport.firePropertyChange("services", new Vector(), services);
145   }
146
147   /**
148    * record this service endpoint so we can use it
149    * 
150    * @param jwsservers
151    * @param srv
152    * @param service2
153    */
154   private void addService(String jwsservers, Services srv, MsaWS service2)
155   {
156     if (services == null)
157     {
158       services = new Vector<Jws2Instance>();
159     }
160     System.out.println("Discovered service: " + jwsservers + " "
161             + srv.toString());
162     services.add(new Jws2Instance(jwsservers, srv.toString(), service2));
163   }
164
165   public class Jws2Instance
166   {
167     public String hosturl;
168
169     public String serviceType;
170
171     public MsaWS service;
172
173     public Jws2Instance(String hosturl, String serviceType, MsaWS service)
174     {
175       super();
176       this.hosturl = hosturl;
177       this.serviceType = serviceType;
178       this.service = service;
179     }
180
181     PresetManager presets = null;
182
183     /**
184      * non thread safe - gets the presets for this service (blocks whilst it
185      * calls the service to get the preset set)
186      * 
187      * @return service presets or null if exceptions were raised.
188      */
189     public PresetManager getPresets()
190     {
191       if (presets == null)
192       {
193         try
194         {
195           presets = service.getPresets();
196         } catch (Exception ex)
197         {
198           System.err
199                   .println("Exception when retrieving presets for service "
200                           + serviceType + " at " + hosturl);
201         }
202       }
203       return presets;
204     }
205
206     public String getHost()
207     {
208       return hosturl;
209       /*
210        * try { URL serviceurl = new URL(hosturl); if (serviceurl.getPort()!=80)
211        * { return serviceurl.getHost()+":"+serviceurl.getPort(); } return
212        * serviceurl.getHost(); } catch (Exception e) {
213        * System.err.println("Failed to parse service URL '" + hosturl +
214        * "' as a valid URL!"); } return null;
215        */
216     }
217
218     /**
219      * @return short description of what the service will do
220      */
221     public String getActionText()
222     {
223       return "Align with " + serviceType;
224     }
225
226     /**
227      * non-thread safe - blocks whilst accessing service to get complete set of
228      * available options and parameters
229      * 
230      * @return
231      */
232     public RunnerConfig getRunnerConfig()
233     {
234       return service.getRunnerOptions();
235     }
236
237     @Override
238     protected void finalize() throws Throwable
239     {
240       if (service != null)
241       {
242         try
243         {
244           Closeable svc = (Closeable) service;
245           service = null;
246           svc.close();
247         } catch (Exception e)
248         {
249         }
250         ;
251       }
252       super.finalize();
253     }
254   };
255
256   /**
257    * holds list of services.
258    */
259   protected Vector<Jws2Instance> services;
260
261   /**
262    * find or add a submenu with the given title in the given menu
263    * 
264    * @param menu
265    * @param submenu
266    * @return the new or existing submenu
267    */
268   private JMenu findOrCreateMenu(JMenu menu, String submenu)
269   {
270     JMenu submenuinstance = null;
271     for (int i = 0, iSize = menu.getMenuComponentCount(); i < iSize; i++)
272     {
273       if (menu.getMenuComponent(i) instanceof JMenu
274               && ((JMenu) menu.getMenuComponent(i)).getText().equals(
275                       submenu))
276       {
277         submenuinstance = (JMenu) menu.getMenuComponent(i);
278       }
279     }
280     if (submenuinstance == null)
281     {
282       submenuinstance = new JMenu(submenu);
283       menu.add(submenuinstance);
284     }
285     return submenuinstance;
286
287   }
288
289   @Override
290   public void attachWSMenuEntry(JMenu wsmenu, final AlignFrame alignFrame)
291   {
292     if (running || services == null || services.size() == 0)
293     {
294       return;
295     }
296     boolean byhost = Cache.getDefault("WSMENU_BYHOST", true), bytype = Cache
297             .getDefault("WSMENU_BYTYPE", true);
298     /**
299      * eventually, JWS2 services will appear under the same align/etc submenus.
300      * for moment we keep them separate.
301      */
302     JMenu atpoint, jws2al = new JMenu("JABA Alignment");
303     MsaWSClient msacl = new MsaWSClient();
304     Vector hostLabels = new Vector();
305     for (final Jws2Instance service : services)
306     {
307       atpoint = jws2al;
308       String host = service.getHost();
309       String type = service.serviceType;
310       if (byhost)
311       {
312         atpoint = findOrCreateMenu(atpoint, host);
313         if (atpoint.getToolTipText() == null)
314         {
315           atpoint.setToolTipText("Services at " + host);
316         }
317       }
318       if (bytype)
319       {
320         atpoint = findOrCreateMenu(atpoint, type);
321         if (atpoint.getToolTipText() == null)
322         {
323           atpoint.setToolTipText(service.getActionText());
324         }
325       }
326       if (!byhost && !hostLabels.contains(host + service.getActionText()))
327       {
328         // add a marker indicating where this service is hosted
329         // relies on services from the same host being listed in a contiguous
330         // group
331         JMenuItem hitm;
332         atpoint.addSeparator();
333         atpoint.add(hitm = new JMenuItem(host));
334         hitm.setForeground(Color.blue);
335         hostLabels.addElement(host);
336       }
337       msacl.attachWSMenuEntry(atpoint, service, alignFrame);
338       /*
339        * JMenuItem sitem = new JMenuItem(service.serviceType);
340        * sitem.setToolTipText("Hosted at " + service.hosturl);
341        * sitem.addActionListener(new ActionListener() {
342        * 
343        * @Override public void actionPerformed(ActionEvent e) { AlignmentView
344        * msa = alignFrame.gatherSequencesForAlignment(); MsaWSClient client =
345        * new MsaWSClient(service, "JWS2 Alignment of " + alignFrame.getTitle(),
346        * msa, false, true, alignFrame.getViewport().getAlignment().getDataset(),
347        * alignFrame); } });
348        */
349     }
350     if (services.size() > 0)
351     {
352       wsmenu.add(jws2al);
353     }
354
355   }
356
357   public static void main(String[] args)
358   {
359     Thread runner = new Thread(getDiscoverer());
360     getDiscoverer().addPropertyChangeListener(new PropertyChangeListener()
361     {
362
363       @Override
364       public void propertyChange(PropertyChangeEvent evt)
365       {
366         System.out.println("Changesupport: There are now "
367                 + getDiscoverer().services.size() + " services");
368       }
369     });
370     runner.start();
371     while (runner.isAlive())
372     {
373       try
374       {
375         Thread.sleep(50);
376       } catch (InterruptedException e)
377       {
378       }
379       ;
380     }
381   }
382
383   private static Jws2Discoverer discoverer;
384
385   public static Jws2Discoverer getDiscoverer()
386   {
387     if (discoverer == null)
388     {
389       discoverer = new Jws2Discoverer();
390     }
391     return discoverer;
392   }
393
394   public boolean hasServices()
395   {
396     // TODO Auto-generated method stub
397     return !running && services != null && services.size() > 0;
398   }
399
400   public boolean isRunning()
401   {
402     return running;
403   }
404
405   /**
406    * the jalview .properties entry for JWS2 URLS
407    */
408   final static String JWS2HOSTURLS = "JWS2HOSTURLS";
409
410   public static void setServiceUrls(Vector<String> urls)
411   {
412     if (urls != null)
413     {
414       StringBuffer urlbuffer = new StringBuffer();
415       String sep = "";
416       for (String url : urls)
417       {
418         urlbuffer.append(sep);
419         urlbuffer.append(url);
420         sep = ",";
421       }
422       Cache.setProperty(JWS2HOSTURLS, urlbuffer.toString());
423     }
424     else
425     {
426       Cache.removeProperty(JWS2HOSTURLS);
427     }
428   }
429
430   public static Vector<String> getServiceUrls()
431   {
432     String surls = Cache.getDefault(JWS2HOSTURLS,
433             "http://www.compbio.dundee.ac.uk/jabaws");
434     Vector<String> urls = new Vector<String>();
435     try
436     {
437       StringTokenizer st = new StringTokenizer(surls, ",");
438       while (st.hasMoreElements())
439       {
440         String url = null;
441         try
442         {
443           java.net.URL u = new java.net.URL(url = st.nextToken());
444           if (!urls.contains(url))
445           {
446             urls.add(url);
447           }
448           else
449           {
450             jalview.bin.Cache.log.info("Ignoring duplicate url in "
451                     + JWS2HOSTURLS + " list");
452           }
453         } catch (Exception ex)
454         {
455           jalview.bin.Cache.log
456                   .warn("Problem whilst trying to make a URL from '"
457                           + ((url != null) ? url : "<null>") + "'");
458           jalview.bin.Cache.log
459                   .warn("This was probably due to a malformed comma separated list"
460                           + " in the "
461                           + JWS2HOSTURLS
462                           + " entry of $(HOME)/.jalview_properties)");
463           jalview.bin.Cache.log.debug("Exception was ", ex);
464         }
465       }
466     } catch (Exception ex)
467     {
468       jalview.bin.Cache.log.warn(
469               "Error parsing comma separated list of urls in "
470                       + JWS2HOSTURLS + " preference.", ex);
471     }
472     if (urls.size() >= 0)
473     {
474       return urls;
475     }
476     return null;
477   }
478
479   public Vector<Jws2Instance> getServices()
480   {
481     return (services == null) ? new Vector<Jws2Instance>()
482             : new Vector<Jws2Instance>(services);
483   }
484
485 }