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