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