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