refactored old jalview service code to its own package.
[jalview.git] / src / jalview / ws / jws1 / Discoverer.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)
3  * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10  * 
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.ws.jws1;
19
20 /**
21  * <p>
22  * Title:
23  * </p>
24  * 
25  * <p>
26  * Description:
27  * </p>
28  * 
29  * <p>
30  * Copyright: Copyright (c) 2004
31  * </p>
32  * 
33  * <p>
34  * Company: Dundee University
35  * </p>
36  * 
37  * @author not attributable
38  * @version 1.0
39  */
40
41 import java.util.*;
42
43 import javax.swing.*;
44
45 import ext.vamsas.*;
46
47 public class Discoverer extends Thread implements Runnable
48 {
49   ext.vamsas.IRegistry registry; // the root registry service.
50
51   private java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
52           this);
53
54   /**
55    * change listeners are notified of "services" property changes
56    * 
57    * @param listener
58    *          to be added that consumes new services Hashtable object.
59    */
60   public void addPropertyChangeListener(
61           java.beans.PropertyChangeListener listener)
62   {
63     changeSupport.addPropertyChangeListener(listener);
64   }
65
66   /**
67    * 
68    * 
69    * @param listener
70    *          to be removed
71    */
72   public void removePropertyChangeListener(
73           java.beans.PropertyChangeListener listener)
74   {
75     changeSupport.removePropertyChangeListener(listener);
76   }
77
78   /**
79    * Property change listener firing routine
80    * 
81    * @param prop
82    *          services
83    * @param oldvalue
84    *          old services hash
85    * @param newvalue
86    *          new services hash
87    */
88   public void firePropertyChange(String prop, Object oldvalue,
89           Object newvalue)
90   {
91     changeSupport.firePropertyChange(prop, oldvalue, newvalue);
92   }
93
94   /**
95    * Initializes the server field with a valid service implementation.
96    * 
97    * @return true if service was located.
98    */
99   private IRegistry locateWebService(java.net.URL WsURL)
100   {
101     IRegistryServiceLocator loc = new IRegistryServiceLocator(); // Default
102     IRegistry server = null;
103     try
104     {
105       server = loc.getRegistryService(WsURL);
106       ((RegistryServiceSoapBindingStub) server).setTimeout(60000); // One
107       // minute
108       // timeout
109     } catch (Exception ex)
110     {
111       jalview.bin.Cache.log
112               .error("Serious!  Service location failed\nfor URL :" + WsURL
113                       + "\n", ex);
114
115       return null;
116     }
117
118     loc.getEngine().setOption("axis", "1");
119
120     return server;
121   }
122
123   static private java.net.URL RootServiceURL = null;
124
125   static public Vector ServiceURLList = null;
126
127   static private boolean reallyDiscoverServices = true;
128
129   public static java.util.Hashtable services = null; // vectors of services
130
131   // stored by
132   // abstractServiceType
133   // string
134
135   public static java.util.Vector serviceList = null; // flat list of services
136
137   static private Vector getDiscoveryURLS()
138   {
139     Vector urls = new Vector();
140     String RootServiceURLs = jalview.bin.Cache
141             .getDefault("DISCOVERY_URLS",
142                     "http://www.compbio.dundee.ac.uk/JalviewWS/services/ServiceRegistry");
143
144     try
145     {
146       StringTokenizer st = new StringTokenizer(RootServiceURLs, ",");
147       while (st.hasMoreElements())
148       {
149         String url = null;
150         try
151         {
152           java.net.URL u = new java.net.URL(url = st.nextToken());
153           if (!urls.contains(u))
154           {
155             urls.add(u);
156           }
157           else
158           {
159             jalview.bin.Cache.log
160                     .info("Ignoring duplicate url in DISCOVERY_URLS list");
161           }
162         } catch (Exception ex)
163         {
164           jalview.bin.Cache.log
165                   .warn("Problem whilst trying to make a URL from '"
166                           + ((url != null) ? url : "<null>") + "'");
167           jalview.bin.Cache.log
168                   .warn("This was probably due to a malformed comma separated list"
169                           + " in the DISCOVERY_URLS entry of $(HOME)/.jalview_properties)");
170           jalview.bin.Cache.log.debug("Exception was ", ex);
171         }
172       }
173     } catch (Exception ex)
174     {
175       jalview.bin.Cache.log
176               .warn(
177                       "Error parsing comma separated list of urls in DISCOVERY_URLS.",
178                       ex);
179     }
180     if (urls.size() > 0)
181     {
182       return urls;
183     }
184     return null;
185   }
186
187   /**
188    * fetch new services or reset to hardwired defaults depending on preferences.
189    */
190   static public void doDiscovery()
191   {
192     jalview.bin.Cache.log
193             .debug("(Re)-Initialising the discovery URL list.");
194     try
195     {
196       reallyDiscoverServices = jalview.bin.Cache.getDefault(
197               "DISCOVERY_START", false);
198       if (reallyDiscoverServices)
199       {
200         ServiceURLList = getDiscoveryURLS();
201       }
202       else
203       {
204         jalview.bin.Cache.log.debug("Setting default services");
205         services = new Hashtable();
206         // Muscle, Clustal and JPred.
207         ServiceHandle[] defServices =
208         {
209             new ServiceHandle(
210                     "MsaWS",
211                     "Edgar, Robert C. (2004), MUSCLE: multiple sequence alignment "
212                             + "with high accuracy and high throughput, Nucleic Acids Research 32(5), 1792-97.",
213                     "http://www.compbio.dundee.ac.uk/JalviewWS/services/MuscleWS",
214                     "Muscle Multiple Protein Sequence Alignment"),
215             new ServiceHandle(
216                     "MsaWS",
217                     "Katoh, K., K. Kuma, K., Toh, H.,  and Miyata, T. (2005) "
218                             + "\"MAFFT version 5: improvement in accuracy of multiple sequence alignment.\""
219                             + " Nucleic Acids Research, 33 511-518",
220                     "http://www.compbio.dundee.ac.uk/JalviewWS/services/MafftWS",
221                     "MAFFT Multiple Sequence Alignment"),
222             new ServiceHandle(
223                     "MsaWS",
224                     "Thompson, J.D., Higgins, D.G. and Gibson, T.J. (1994) CLUSTAL W: improving the sensitivity of progressive multiple"
225                             + " sequence alignment through sequence weighting, position specific gap penalties and weight matrix choice."
226                             + " Nucleic Acids Research, 22 4673-4680",
227                     "http://www.compbio.dundee.ac.uk/JalviewWS/services/ClustalWS",
228                     "ClustalW Multiple Sequence Alignment"),
229             new ServiceHandle(
230                     "SecStrPred",
231                     "Cole C., Barber J. D., Barton G.J (2008) "
232                             + "The Jpred 3 secondary structure prediction server "
233                             + "Nucleic Acids Research, 36 W197-W201",
234                     "http://www.compbio.dundee.ac.uk/JalviewWS/services/jpred",
235                     "JNet Secondary Structure Prediction") };
236         services = new Hashtable();
237         serviceList = new Vector();
238         buildServiceLists(defServices, serviceList, services);
239       }
240
241     } catch (Exception e)
242     {
243       System.err
244               .println("jalview.rootRegistry is not a proper url!\nWas set to "
245                       + RootServiceURL + "\n" + e);
246     }
247
248   }
249
250   // TODO: JBPNote : make this discover more services based on list of
251   // discovery service urls, break cyclic references to the same url and
252   // duplicate service entries (same endpoint *and* same interface)
253   private ServiceHandle[] getServices(java.net.URL location)
254   {
255     ServiceHandles shs = null;
256     try
257     {
258       jalview.bin.Cache.log.debug("Discovering services using " + location);
259       shs = locateWebService(location).getServices();
260     } catch (org.apache.axis.AxisFault f)
261     {
262       // JBPNote - should do this a better way!
263       if (f.getFaultReason().indexOf("(407)") > -1)
264       {
265         if (jalview.gui.Desktop.desktop != null)
266         {
267           JOptionPane
268                   .showMessageDialog(
269                           jalview.gui.Desktop.desktop,
270                           "Please set up your proxy settings in the 'Connections' tab of the Preferences window",
271                           "Proxy Authorization Failed",
272                           JOptionPane.WARNING_MESSAGE);
273         }
274       }
275       else
276       {
277         jalview.bin.Cache.log.warn("No Discovery service at " + location);
278         jalview.bin.Cache.log.debug("Axis Fault", f);
279       }
280     } catch (Exception e)
281     {
282       jalview.bin.Cache.log.warn("No Discovery service at " + location);
283       jalview.bin.Cache.log.debug("Discovery Service General Exception", e);
284     }
285     if ((shs != null) && shs.getServices().length > 0)
286     {
287       return shs.getServices();
288     }
289     return null;
290   }
291
292   /**
293    * Adds a list of services to the service catalog and categorised catalog
294    * returns true if ServiceURLList was modified with a new DiscoveryService URL
295    * 
296    * @param sh
297    *          ServiceHandle[]
298    * @param cat
299    *          Vector
300    * @param sscat
301    *          Hashtable
302    * @return boolean
303    */
304   static private boolean buildServiceLists(ServiceHandle[] sh, Vector cat,
305           Hashtable sscat)
306   {
307     boolean seenNewDiscovery = false;
308     for (int i = 0, j = sh.length; i < j; i++)
309     {
310       if (!cat.contains(sh[i]))
311       {
312         jalview.bin.Cache.log.debug("A " + sh[i].getAbstractName()
313                 + " service called " + sh[i].getName() + " exists at "
314                 + sh[i].getEndpointURL() + "\n");
315         if (!sscat.containsKey(sh[i].getAbstractName()))
316         {
317           sscat.put(sh[i].getAbstractName(), cat = new Vector());
318         }
319         else
320         {
321           cat = (Vector) sscat.get(sh[i].getAbstractName());
322         }
323         cat.add(sh[i]);
324         if (sh[i].getAbstractName().equals("Registry"))
325         {
326           for (int s = 0, sUrls = ServiceURLList.size(); s < sUrls; s++)
327           {
328             java.net.URL disc_serv = null;
329             try
330             {
331               disc_serv = new java.net.URL(sh[i].getEndpointURL());
332               if (!ServiceURLList.contains(disc_serv))
333               {
334                 jalview.bin.Cache.log
335                         .debug("Adding new discovery service at "
336                                 + disc_serv);
337                 ServiceURLList.add(disc_serv);
338                 seenNewDiscovery = true;
339               }
340             } catch (Exception e)
341             {
342               jalview.bin.Cache.log.debug(
343                       "Ignoring bad discovery service URL "
344                               + sh[i].getEndpointURL(), e);
345             }
346           }
347         }
348       }
349     }
350     return seenNewDiscovery;
351   }
352
353   public void discoverServices()
354   {
355     Hashtable sscat = new Hashtable();
356     Vector cat = new Vector();
357     ServiceHandle sh[] = null;
358     int s_url = 0;
359     if (ServiceURLList == null)
360     {
361       jalview.bin.Cache.log
362               .debug("No service endpoints to use for service discovery.");
363       return;
364     }
365     while (s_url < ServiceURLList.size())
366     {
367       if ((sh = getServices((java.net.URL) ServiceURLList.get(s_url))) != null)
368       {
369
370         buildServiceLists(sh, cat, sscat);
371       }
372       else
373       {
374         jalview.bin.Cache.log
375                 .warn("No services at "
376                         + ((java.net.URL) ServiceURLList.get(s_url))
377                         + " - check DISCOVERY_URLS property in .jalview_properties");
378       }
379       s_url++;
380     }
381     // TODO: decide on correct semantics for services list - PropertyChange
382     // provides a way of passing the new object around
383     // so no need to access original discovery thread.
384     // Curent decision is to change properties then notify listeners with old
385     // and new values.
386     Hashtable oldServices = services;
387     // Vector oldServicelist = serviceList;
388     services = sscat;
389     serviceList = cat;
390     firePropertyChange("services", oldServices, services);
391   }
392
393   /**
394    * creates a new thread to call discoverServices()
395    */
396   public void run()
397   {
398     final Discoverer discoverer = this;
399     Thread discoverThread = new Thread()
400     {
401       public void run()
402       {
403         discoverer.doDiscovery();
404         discoverer.discoverServices();
405       }
406     };
407     discoverThread.start();
408   }
409
410   /**
411    * binding service abstract name to handler class
412    */
413   private static Hashtable serviceClientBindings;
414
415   public static WS1Client getServiceClient(ServiceHandle sh)
416   {
417     if (serviceClientBindings == null)
418     {
419       // get a list from Config or create below
420       serviceClientBindings = new Hashtable();
421       serviceClientBindings.put("MsaWS", new MsaWSClient());
422       serviceClientBindings.put("SecStrPred", new JPredClient());
423       serviceClientBindings.put("SeqSearch", new SeqSearchWSClient());
424     }
425     WS1Client instance = (WS1Client) serviceClientBindings.get(sh
426             .getAbstractName());
427     if (instance == null)
428     {
429       System.err
430               .println("WARNING - POSSIBLE IMPLEMENTATION ERROR - cannot find WSClient implementation for "
431                       + sh.getAbstractName());
432     }
433     else
434     {
435       instance.serviceHandle = sh;
436     }
437     return instance;
438   }
439   /**
440    * notes on discovery service 1. need to allow multiple discovery source urls.
441    * 2. user interface to add/control list of urls in preferences notes on
442    * wsclient discovery 1. need a classpath property with list of additional
443    * plugin directories 2. optional config to cite specific bindings between
444    * class name and Abstract service name. 3. precedence for automatic discovery
445    * by using getAbstractName for WSClient - user added plugins override default
446    * plugins ? notes on wsclient gui code for gui attachment now moved to
447    * wsclient implementation. Needs more abstraction but approach seems to work.
448    * is it possible to 'generalise' the data retrieval calls further ? current
449    * methods are very specific (gatherForMSA or gatherForSeqOrMsaSecStrPred),
450    * new methods for conservation (group or alignment), treecalc (aligned
451    * profile), seqannot (sequences selected from dataset, annotation back to
452    * dataset).
453    * 
454    */
455 }