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