Clover icon

Coverage Report

  1. Project Clover database Wed Dec 3 2025 17:03:17 GMT
  2. Package jalview.ws.jws2

File Jws2Discoverer.java

 

Coverage histogram

../../../img/srcFileCovDistChart6.png
37% of files have more coverage

Code metrics

88
199
25
1
715
524
93
0.47
7.96
25
3.72

Classes

Class Line # Actions
Jws2Discoverer 73 199 93
0.544871854.5%
 

Contributing tests

This file is covered by 343 tests. .

Source view

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.jws2;
22   
23    import jalview.bin.Cache;
24    import jalview.bin.Console;
25    import jalview.bin.ApplicationSingletonProvider;
26    import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
27    import jalview.gui.AlignFrame;
28    import jalview.util.MessageManager;
29    import jalview.ws.ServiceChangeListener;
30    import jalview.ws.WSDiscovererI;
31    import jalview.ws.api.ServiceWithParameters;
32    import jalview.ws.jws2.jabaws2.Jws2Instance;
33    import jalview.ws.params.ParamDatastoreI;
34   
35    import java.beans.PropertyChangeEvent;
36    import java.beans.PropertyChangeListener;
37    import java.beans.PropertyChangeSupport;
38    import java.net.MalformedURLException;
39    import java.net.URL;
40    import java.util.ArrayList;
41    import java.util.Collections;
42    import java.util.HashSet;
43    import java.util.List;
44    import java.util.Set;
45    import java.util.StringTokenizer;
46    import java.util.Vector;
47    import java.util.concurrent.CompletableFuture;
48    import java.util.concurrent.CopyOnWriteArraySet;
49    import java.util.concurrent.ExecutionException;
50    import java.util.concurrent.Future;
51    import java.util.concurrent.FutureTask;
52   
53    import javax.swing.JMenu;
54   
55    import compbio.ws.client.Services;
56    import jalview.bin.Cache;
57    import jalview.bin.Console;
58    import jalview.gui.AlignFrame;
59    import jalview.gui.Desktop;
60    import jalview.gui.JvSwingUtils;
61    import jalview.util.MessageManager;
62    import jalview.ws.WSMenuEntryProviderI;
63    import jalview.ws.jws2.jabaws2.Jws2Instance;
64    import jalview.ws.params.ParamDatastoreI;
65   
66    /**
67    * discoverer for jws2 services. Follows the lightweight service discoverer
68    * pattern (archetyped by EnfinEnvision2OneWay)
69    *
70    * @author JimP
71    *
72    */
 
73    public class Jws2Discoverer implements WSDiscovererI, Runnable, ApplicationSingletonI
74    {
75    /**
76    * Returns the singleton instance of this class.
77    *
78    * @return
79    */
 
80  9048 toggle public static Jws2Discoverer getInstance()
81    {
82  9048 return ApplicationSingletonProvider.getInstance(Jws2Discoverer.class);
83    }
84    public static final String COMPBIO_JABAWS = "http://www.compbio.dundee.ac.uk/jabaws";
85   
86    /*
87    * the .jalview_properties entry for JWS2 URLS
88    */
89    private final static String JWS2HOSTURLS = "JWS2HOSTURLS";
90   
91    /*
92    * Override for testing only
93    */
94    private List<String> testUrls = null;
95   
96    // preferred url has precedence over others
97    private String preferredUrl;
98   
99   
100    private Set<ServiceChangeListener> serviceListeners = new CopyOnWriteArraySet<>();
101   
102    private Vector<String> invalidServiceUrls = null;
103   
104    private Vector<String> urlsWithoutServices = null;
105   
106    private Vector<String> validServiceUrls = null;
107   
108    private volatile boolean running = false;
109   
110    private volatile boolean aborted = false;
111   
112   
113    private volatile Thread oldthread = null;
114   
115    /**
116    * holds list of services.
117    */
118    protected Vector<Jws2Instance> services;
119   
120    /**
121    * Private constructor enforces use of singleton via getDiscoverer()
122    */
 
123  13 toggle private Jws2Discoverer()
124    {
125    }
126   
 
127  439 toggle @Override
128    public void addServiceChangeListener(ServiceChangeListener listener)
129    {
130  439 serviceListeners.add(listener);
131    }
132   
 
133  326 toggle @Override
134    public void removeServiceChangeListener(ServiceChangeListener listener)
135    {
136  326 serviceListeners.remove(listener);
137    }
138   
 
139  324 toggle private void notifyServiceListeners(List<? extends ServiceWithParameters> services)
140    {
141  11 if (services == null) services = this.services;
142  324 for (var listener : serviceListeners) {
143  7658 listener.servicesChanged(this, services);
144    }
145    }
146   
147    /**
148    * @return the aborted
149    */
 
150  0 toggle public boolean isAborted()
151    {
152  0 return aborted;
153    }
154   
155    /**
156    * @param aborted
157    * the aborted to set
158    */
 
159  76 toggle public void setAborted(boolean aborted)
160    {
161  76 this.aborted = aborted;
162    }
163   
 
164  166 toggle @Override
165    public void run()
166    {
167   
168  166 if (running && oldthread != null && oldthread.isAlive())
169    {
170  34 if (!aborted)
171    {
172  2 return;
173    }
174  105 while (running)
175    {
176  73 try
177    {
178  73 Console.debug(
179    "Waiting around for old discovery thread to finish.");
180    // wait around until old discoverer dies
181  73 Thread.sleep(100);
182    } catch (Exception e)
183    {
184    }
185    }
186  32 aborted = false;
187  32 Console.debug("Old discovery thread has finished.");
188    }
189  164 running = true;
190   
191    // first set up exclusion list if needed
192  164 final Set<String> ignoredServices = new HashSet<>();
193  164 for (String ignored : Cache
194    .getDefault("IGNORED_JABAWS_SERVICETYPES", "").split("\\|"))
195    {
196  164 ignoredServices.add(ignored);
197    }
198   
199  164 notifyServiceListeners(Collections.emptyList());
200  164 oldthread = Thread.currentThread();
201  164 try
202    {
203  164 getClass().getClassLoader().loadClass("compbio.ws.client.Jws2Client");
204    } catch (ClassNotFoundException e)
205    {
206  0 jalview.bin.Console.errPrintln(
207    "Not enabling JABA Webservices : client jar is not available."
208    + "\nPlease check that your webstart JNLP file is up to date!");
209  0 running = false;
210  0 return;
211    }
212    // reinitialise records of good and bad service URLs
213  164 if (services != null)
214    {
215  144 services.removeAllElements();
216    }
217  164 if (urlsWithoutServices != null)
218    {
219  0 urlsWithoutServices.removeAllElements();
220    }
221  164 if (invalidServiceUrls != null)
222    {
223  0 invalidServiceUrls.removeAllElements();
224    }
225  164 if (validServiceUrls != null)
226    {
227  144 validServiceUrls.removeAllElements();
228    }
229  164 ArrayList<String> svctypes = new ArrayList<>();
230   
231  164 List<JabaWsServerQuery> qrys = new ArrayList<>();
232  164 for (final String jwsserver : getServiceUrls())
233    {
234  164 JabaWsServerQuery squery = new JabaWsServerQuery(this, jwsserver);
235  164 if (svctypes.size() == 0)
236    {
237    // TODO: remove this ugly hack to get Canonical JABA service ordering
238    // for all possible services
239  164 for (Services sv : squery.JABAWS2SERVERS)
240    {
241  1804 if (!ignoredServices.contains(sv.toString()))
242    {
243  1804 svctypes.add(sv.toString());
244    }
245    }
246   
247    }
248  164 qrys.add(squery);
249  164 new Thread(squery).start();
250    }
251  164 boolean finished = true;
252  164 do
253    {
254  473 finished = true;
255  473 try
256    {
257  473 Thread.sleep(100);
258    } catch (Exception e)
259    {
260    }
261  469 for (JabaWsServerQuery squery : qrys)
262    {
263  469 if (squery.isRunning())
264    {
265  444 finished = false;
266    }
267    }
268  469 if (aborted)
269    {
270  145 Console.debug(
271    "Aborting " + qrys.size() + " JABAWS discovery threads.");
272  145 for (JabaWsServerQuery squery : qrys)
273    {
274  145 squery.setQuit(true);
275    }
276    }
277  469 } while (!aborted && !finished);
278  160 if (!aborted)
279    {
280    // resort services according to order found in jabaws service list
281    // also ensure services for each host are ordered in same way.
282   
283  15 if (services != null && services.size() > 0)
284    {
285  13 Jws2Instance[] svcs = new Jws2Instance[services.size()];
286  13 int[] spos = new int[services.size()];
287  13 int ipos = 0;
288  13 List<String> svcUrls = getServiceUrls();
289  13 for (Jws2Instance svc : services)
290    {
291  183 svcs[ipos] = svc;
292  183 spos[ipos++] = 1000 * svcUrls.indexOf(svc.getHostURL()) + 1
293    + svctypes.indexOf(svc.getName());
294    }
295  13 jalview.util.QuickSort.sort(spos, svcs);
296  13 services = new Vector<>();
297  13 for (Jws2Instance svc : svcs)
298    {
299  183 if (!ignoredServices.contains(svc.getName()))
300    {
301  183 services.add(svc);
302    }
303    }
304    }
305    }
306  160 oldthread = null;
307  160 running = false;
308  160 notifyServiceListeners(services);
309    }
310   
311    /**
312    * record this service endpoint so we can use it
313    *
314    * @param jwsservers
315    * @param srv
316    * @param service2
317    */
 
318  550 toggle synchronized void addService(String jwsservers, Jws2Instance service)
319    {
320  550 if (services == null)
321    {
322  6 services = new Vector<>();
323    }
324  550 jalview.bin.Console.info(
325    "Discovered service: " + jwsservers + " " + service.toString());
326    // Jws2Instance service = new Jws2Instance(jwsservers, srv.toString(),
327    // service2);
328   
329  550 services.add(service);
330    // retrieve the presets and parameter set and cache now
331  550 ParamDatastoreI pds = service.getParamStore();
332  549 if (pds != null)
333    {
334  549 pds.getPresets();
335    }
336  549 service.hasParameters();
337  549 if (validServiceUrls == null)
338    {
339  5 validServiceUrls = new Vector<>();
340    }
341  549 validServiceUrls.add(jwsservers);
342    }
343   
344    /**
345    *
346    * @param args
347    * @j2sIgnore
348    */
 
349  0 toggle public static void main(String[] args)
350    {
351  0 Jws2Discoverer instance = getInstance();
352  0 if (args.length > 0)
353    {
354  0 instance.testUrls = new ArrayList<>();
355  0 for (String url : args)
356    {
357  0 instance.testUrls.add(url);
358    }
359    }
360  0 var discoverer = getInstance();
361  0 discoverer.addServiceChangeListener((_discoverer, _services) -> {
362  0 if (discoverer.services != null)
363    {
364  0 jalview.bin.Console
365    .outPrintln("Changesupport: There are now "
366    + discoverer.services.size()
367    + " services");
368  0 int i = 1;
369  0 for (ServiceWithParameters s_instance : discoverer.services)
370    {
371  0 jalview.bin.Console.outPrintln(
372    "Service " + i++ + " " + s_instance.getClass()
373    + "@" + s_instance.getHostURL() + ": "
374    + s_instance.getActionText());
375    }
376   
377    }
378    });
379  0 try
380    {
381  0 discoverer.startDiscoverer().get();
382    } catch (InterruptedException | ExecutionException e)
383    {
384    }
385  0 try
386    {
387  0 Thread.sleep(50);
388    } catch (InterruptedException x)
389    {
390    }
391    }
392   
393   
 
394  12933 toggle @Override
395    public boolean hasServices()
396    {
397  12933 return !running && services != null && services.size() > 0;
398    }
399   
 
400  8259 toggle @Override
401    public boolean isRunning()
402    {
403  8259 return running;
404    }
405   
 
406  0 toggle @Override
407    public void setServiceUrls(List<String> wsUrls)
408    {
409  0 if (wsUrls != null && !wsUrls.isEmpty())
410    {
411  0 StringBuilder urls = new StringBuilder(128);
412  0 String sep = "";
413  0 for (String url : wsUrls)
414    {
415  0 urls.append(sep);
416  0 urls.append(url);
417  0 sep = ",";
418    }
419  0 Cache.setProperty(JWS2HOSTURLS, urls.toString());
420    }
421    else
422    {
423  0 Cache.removeProperty(JWS2HOSTURLS);
424    }
425    }
426   
427    /**
428    * Returns web service URLs, in the order in which they should be tried (or an
429    * empty list).
430    *
431    * @return
432    */
 
433  188 toggle @Override
434    public List<String> getServiceUrls()
435    {
436  188 if (testUrls != null)
437    {
438    // return test urls, if there are any, instead of touching cache
439  0 return testUrls;
440    }
441  188 List<String> urls = new ArrayList<>();
442   
443  188 if (this.preferredUrl != null)
444    {
445  0 urls.add(preferredUrl);
446    }
447   
448  188 String surls = Cache.getDefault(JWS2HOSTURLS, COMPBIO_JABAWS);
449  188 try
450    {
451  188 StringTokenizer st = new StringTokenizer(surls, ",");
452  376 while (st.hasMoreElements())
453    {
454  188 String url = null;
455  188 try
456    {
457  188 url = st.nextToken();
458  188 new URL(url);
459  188 if (!urls.contains(url))
460    {
461  188 urls.add(url);
462    }
463    else
464    {
465  0 Console.warn("Ignoring duplicate url " + url + " in "
466    + JWS2HOSTURLS + " list");
467    }
468    } catch (MalformedURLException ex)
469    {
470  0 Console.warn("Problem whilst trying to make a URL from '"
471  0 + ((url != null) ? url : "<null>") + "'");
472  0 Console.warn(
473    "This was probably due to a malformed comma separated list"
474    + " in the " + JWS2HOSTURLS
475    + " entry of $(HOME)/.jalview_properties)");
476  0 Console.debug("Exception was ", ex);
477    }
478    }
479    } catch (Exception ex)
480    {
481  0 Console.warn("Error parsing comma separated list of urls in "
482    + JWS2HOSTURLS + " preference.", ex);
483    }
484  188 return urls;
485    }
486   
 
487  3360 toggle @Override
488    public Vector<ServiceWithParameters> getServices()
489    {
490  3360 return (services == null) ? new Vector<>() : new Vector<>(services);
491    }
492   
493    /**
494    * test the given URL with the JabaWS test code
495    *
496    * @param foo
497    * @return
498    */
 
499  0 toggle @Override
500    public boolean testServiceUrl(URL foo)
501    {
502  0 try
503    {
504  0 compbio.ws.client.WSTester
505    .main(new String[]
506    { "-h=" + foo.toString() });
507    } catch (Exception e)
508    {
509  0 e.printStackTrace();
510  0 return false;
511    } catch (OutOfMemoryError e)
512    {
513  0 e.printStackTrace();
514  0 return false;
515    } catch (Error e)
516    {
517  0 e.printStackTrace();
518  0 return false;
519    }
520   
521  0 return true;
522    }
523   
 
524  0 toggle public boolean restart()
525    {
526  0 synchronized (this)
527    {
528  0 if (running)
529    {
530  0 aborted = true;
531    }
532    else
533    {
534  0 running = true;
535    }
536  0 return aborted;
537    }
538    }
539   
540    /**
541    * Start a fresh discovery thread and notify the given object when we're
542    * finished. Any known existing threads will be killed before this one is
543    * started.
544    *
545    * @param changeSupport2
546    * @return new thread
547    */
 
548  166 toggle @Override
549    public CompletableFuture<WSDiscovererI> startDiscoverer()
550    {
551    /* if (restart())
552    {
553    return;
554    }
555    else
556    {
557    Thread thr = new Thread(this);
558    thr.start();
559    }
560    */
561  166 if (isRunning())
562    {
563  76 setAborted(true);
564    }
565  166 CompletableFuture<WSDiscovererI> task = CompletableFuture
566    .supplyAsync(() -> {
567  166 run();
568  162 return Jws2Discoverer.this;
569    });
570  166 return task;
571    }
572   
573    /**
574    * @return the invalidServiceUrls
575    */
 
576  0 toggle public Vector<String> getInvalidServiceUrls()
577    {
578  0 return invalidServiceUrls;
579    }
580   
581    /**
582    * @return the urlsWithoutServices
583    */
 
584  0 toggle public Vector<String> getUrlsWithoutServices()
585    {
586  0 return urlsWithoutServices;
587    }
588   
589    /**
590    * add an 'empty' JABA server to the list. Only servers not already in the
591    * 'bad URL' list will be added to this list.
592    *
593    * @param jwsservers
594    */
 
595  0 toggle public synchronized void addUrlwithnoservices(String jwsservers)
596    {
597  0 if (urlsWithoutServices == null)
598    {
599  0 urlsWithoutServices = new Vector<>();
600    }
601   
602  0 if ((invalidServiceUrls == null
603    || !invalidServiceUrls.contains(jwsservers))
604    && !urlsWithoutServices.contains(jwsservers))
605    {
606  0 urlsWithoutServices.add(jwsservers);
607    }
608    }
609   
610    /**
611    * add a bad URL to the list
612    *
613    * @param jwsservers
614    */
 
615  0 toggle public synchronized void addInvalidServiceUrl(String jwsservers)
616    {
617  0 if (invalidServiceUrls == null)
618    {
619  0 invalidServiceUrls = new Vector<>();
620    }
621  0 if (!invalidServiceUrls.contains(jwsservers))
622    {
623  0 invalidServiceUrls.add(jwsservers);
624    }
625    }
626   
627    /**
628    *
629    * @return a human readable report of any problems with the service URLs used
630    * for discovery
631    */
 
632  0 toggle @Override
633    public String getErrorMessages()
634    {
635  0 if (!isRunning() && !isAborted())
636    {
637  0 StringBuffer ermsg = new StringBuffer();
638  0 boolean list = false;
639  0 if (getInvalidServiceUrls() != null
640    && getInvalidServiceUrls().size() > 0)
641    {
642  0 ermsg.append(MessageManager.getString("warn.urls_not_contacted")
643    + ": \n");
644  0 for (String svcurl : getInvalidServiceUrls())
645    {
646  0 if (list)
647    {
648  0 ermsg.append(", ");
649    }
650  0 list = true;
651  0 ermsg.append(svcurl);
652    }
653  0 ermsg.append("\n\n");
654    }
655  0 list = false;
656  0 if (getUrlsWithoutServices() != null
657    && getUrlsWithoutServices().size() > 0)
658    {
659  0 ermsg.append(
660    MessageManager.getString("warn.urls_no_jaba") + ": \n");
661  0 for (String svcurl : getUrlsWithoutServices())
662    {
663  0 if (list)
664    {
665  0 ermsg.append(", ");
666    }
667  0 list = true;
668  0 ermsg.append(svcurl);
669    }
670  0 ermsg.append("\n");
671    }
672  0 if (ermsg.length() > 1)
673    {
674  0 return ermsg.toString();
675    }
676   
677    }
678  0 return null;
679    }
680   
 
681  11 toggle @Override
682    public int getServerStatusFor(String url)
683    {
684  11 if (validServiceUrls != null && validServiceUrls.contains(url))
685    {
686  0 return STATUS_OK;
687    }
688  11 if (urlsWithoutServices != null && urlsWithoutServices.contains(url))
689    {
690  0 return STATUS_NO_SERVICES;
691    }
692  11 if (invalidServiceUrls != null && invalidServiceUrls.contains(url))
693    {
694  0 return STATUS_INVALID;
695    }
696  11 return STATUS_UNKNOWN;
697    }
698   
699    /**
700    * Set a URL to try before any others. For use with command-line parameter to
701    * configure a local Jabaws installation without the need to add to property
702    * files.
703    *
704    * @param value
705    * @throws MalformedURLException
706    */
 
707  2 toggle public void setPreferredUrl(String value) throws MalformedURLException
708    {
709  2 if (value != null && value.trim().length() > 0)
710    {
711  2 new URL(value);
712  2 preferredUrl = value;
713    }
714    }
715    }