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