JAL-1379 JAL-1378 exclusion list property to ignore services not fully integrated...
[jalview.git] / src / jalview / ws / jws2 / Jws2Discoverer.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3  * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, 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.jws2;
19
20 import jalview.bin.Cache;
21 import jalview.gui.AlignFrame;
22 import jalview.gui.Desktop;
23 import jalview.gui.JvSwingUtils;
24 import jalview.ws.WSMenuEntryProviderI;
25 import jalview.ws.jws2.jabaws2.Jws2Instance;
26 import jalview.ws.params.ParamDatastoreI;
27
28 import java.awt.Color;
29 import java.awt.event.ActionEvent;
30 import java.awt.event.ActionListener;
31 import java.beans.PropertyChangeEvent;
32 import java.beans.PropertyChangeListener;
33 import java.net.URL;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.HashMap;
37 import java.util.HashSet;
38 import java.util.Hashtable;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Set;
42 import java.util.StringTokenizer;
43 import java.util.Vector;
44
45 import javax.swing.JMenu;
46 import javax.swing.JMenuItem;
47
48 import compbio.ws.client.Services;
49
50 /**
51  * discoverer for jws2 services. Follows the lightweight service discoverer
52  * pattern (archetyped by EnfinEnvision2OneWay)
53  * 
54  * @author JimP
55  * 
56  */
57 public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
58 {
59   private java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
60           this);
61
62   /**
63    * change listeners are notified of "services" property changes
64    * 
65    * @param listener
66    *          to be added that consumes new services Hashtable object.
67    */
68   public void addPropertyChangeListener(
69           java.beans.PropertyChangeListener listener)
70   {
71     changeSupport.addPropertyChangeListener(listener);
72   }
73
74   /**
75    * 
76    * 
77    * @param listener
78    *          to be removed
79    */
80   public void removePropertyChangeListener(
81           java.beans.PropertyChangeListener listener)
82   {
83     changeSupport.removePropertyChangeListener(listener);
84   }
85
86   boolean running = false, aborted = false;
87
88   /**
89    * @return the aborted
90    */
91   public boolean isAborted()
92   {
93     return aborted;
94   }
95
96   /**
97    * @param aborted
98    *          the aborted to set
99    */
100   public void setAborted(boolean aborted)
101   {
102     this.aborted = aborted;
103   }
104
105   Thread oldthread = null;
106
107   public void run()
108   {
109
110     if (running && oldthread != null && oldthread.isAlive())
111     {
112       if (!aborted)
113       {
114         return;
115       }
116       while (running)
117       {
118         try
119         {
120           Cache.log
121                   .debug("Waiting around for old discovery thread to finish.");
122           // wait around until old discoverer dies
123           Thread.sleep(100);
124         } catch (Exception e)
125         {
126         }
127       }
128       Cache.log.debug("Old discovery thread has finished.");
129     }
130     running = true;
131     
132     // first set up exclusion list if needed
133     final Set<String> ignoredServices = new HashSet<String>();
134     for (String ignored:jalview.bin.Cache.getDefault("IGNORED_JABAWS_SERVICETYPES", Services.JpredWS.toString()).split("\\|"))
135     {
136       ignoredServices.add(ignored);
137     }
138
139     
140     changeSupport.firePropertyChange("services", services, new Vector());
141     oldthread = Thread.currentThread();
142     try
143     {
144       Class foo = getClass().getClassLoader().loadClass(
145               "compbio.ws.client.Jws2Client");
146     } catch (ClassNotFoundException e)
147     {
148       System.err
149               .println("Not enabling JABA Webservices : client jar is not available."
150                       + "\nPlease check that your webstart JNLP file is up to date!");
151       running = false;
152       return;
153     }
154     // reinitialise records of good and bad service URLs
155     if (services != null)
156     {
157       services.removeAllElements();
158     }
159     if (urlsWithoutServices != null)
160     {
161       urlsWithoutServices.removeAllElements();
162     }
163     if (invalidServiceUrls != null)
164     {
165       invalidServiceUrls.removeAllElements();
166     }
167     if (validServiceUrls != null)
168     {
169       validServiceUrls.removeAllElements();
170     }
171     ArrayList<String> svctypes = new ArrayList<String>();
172
173     List<JabaWsServerQuery> qrys = new ArrayList<JabaWsServerQuery>();
174     for (final String jwsservers : getServiceUrls())
175     {
176       JabaWsServerQuery squery = new JabaWsServerQuery(this, jwsservers);
177       if (svctypes.size() == 0)
178       {
179         // TODO: remove this ugly hack to get Canonical JABA service ordering
180         // for all possible services
181         for (Services sv : squery.JABAWS2SERVERS)
182         {
183           if (!ignoredServices.contains(sv.toString()))
184           {
185             svctypes.add(sv.toString());
186           }
187         }
188
189       }
190       qrys.add(squery);
191       new Thread(squery).start();
192     }
193     boolean finished = true;
194     do
195     {
196       finished = true;
197       try
198       {
199         Thread.sleep(100);
200       } catch (Exception e)
201       {
202       }
203       ;
204       for (JabaWsServerQuery squery : qrys)
205       {
206         if (squery.isRunning()){
207           finished=false;
208         }
209       }
210       if (aborted)
211       {
212         Cache.log.debug("Aborting " + qrys.size()
213                 + " JABAWS discovery threads.");
214         for (JabaWsServerQuery squery : qrys)
215         {
216           squery.setQuit(true);
217         }
218       }
219     } while (!aborted && !finished);
220     if (!aborted)
221     {
222       // resort services according to order found in jabaws service list
223       // also ensure servics for each host are ordered in same way.
224
225       if (services != null && services.size() > 0)
226       {
227         Jws2Instance[] svcs = new Jws2Instance[services.size()];
228         int[] spos = new int[services.size()];
229         int ipos = 0;
230         Vector svcUrls = getServiceUrls();
231         for (Jws2Instance svc : services)
232         {
233           svcs[ipos] = svc;
234           spos[ipos++] = 1000 * svcUrls.indexOf(svc.getHost()) + 1
235                   + svctypes.indexOf(svc.serviceType);
236         }
237         jalview.util.QuickSort.sort(spos, svcs);
238         services = new Vector<Jws2Instance>();
239         for (Jws2Instance svc : svcs)
240         {
241           if (!ignoredServices.contains(svc.serviceType))
242           {
243             services.add(svc);
244           }
245         }
246       }
247     }
248     oldthread = null;
249     running = false;
250     changeSupport.firePropertyChange("services", new Vector(), services);
251   }
252
253   /**
254    * record this service endpoint so we can use it
255    * 
256    * @param jwsservers
257    * @param srv
258    * @param service2
259    */
260   synchronized void addService(String jwsservers, Jws2Instance service)
261   {
262     if (services == null)
263     {
264       services = new Vector<Jws2Instance>();
265     }
266     System.out.println("Discovered service: " + jwsservers + " "
267             + service.toString());
268     // Jws2Instance service = new Jws2Instance(jwsservers, srv.toString(),
269     // service2);
270
271     services.add(service);
272     // retrieve the presets and parameter set and cache now
273     ParamDatastoreI pds = service.getParamStore();
274     if (pds != null)
275     {
276       pds.getPresets();
277     }
278     service.hasParameters();
279     if (validServiceUrls == null)
280     {
281       validServiceUrls = new Vector();
282     }
283     validServiceUrls.add(jwsservers);
284   }
285
286   /**
287    * holds list of services.
288    */
289   protected Vector<Jws2Instance> services;
290
291   /**
292    * attach all available web services to the appropriate submenu in the given
293    * JMenu
294    */
295   public void attachWSMenuEntry(JMenu wsmenu, final AlignFrame alignFrame)
296   {
297     // dynamically regenerate service list.
298     populateWSMenuEntry(wsmenu, alignFrame, null);
299   }
300
301   private boolean isRecalculable(String action)
302   {
303     return (action != null && action.equalsIgnoreCase("conservation"));
304   }
305
306   private void populateWSMenuEntry(JMenu jws2al,
307           final AlignFrame alignFrame, String typeFilter)
308   {
309     if (running || services == null || services.size() == 0)
310     {
311       return;
312     }
313     boolean byhost = Cache.getDefault("WSMENU_BYHOST", false), bytype = Cache
314             .getDefault("WSMENU_BYTYPE", false);
315     /**
316      * eventually, JWS2 services will appear under the same align/etc submenus.
317      * for moment we keep them separate.
318      */
319     JMenu atpoint;
320     List<Jws2Instance> enumerableServices = new ArrayList<Jws2Instance>();
321     // jws2al.removeAll();
322     Map<String, Jws2Instance> preferredHosts = new HashMap<String, Jws2Instance>();
323     Map<String, List<Jws2Instance>> alternates = new HashMap<String, List<Jws2Instance>>();
324     for (Jws2Instance service : services.toArray(new Jws2Instance[0]))
325     {
326       if (!isRecalculable(service.action))
327       {
328         // add 'one shot' services to be displayed using the classic menu
329         // structure
330         enumerableServices.add(service);
331       }
332       else
333       {
334         if (!preferredHosts.containsKey(service.serviceType))
335         {
336           Jws2Instance preferredInstance = getPreferredServiceFor(
337                   alignFrame, service.serviceType);
338           if (preferredInstance != null)
339           {
340             preferredHosts.put(service.serviceType, preferredInstance);
341           }
342           else
343           {
344             preferredHosts.put(service.serviceType, service);
345           }
346         }
347         List<Jws2Instance> ph = alternates.get(service.serviceType);
348         if (preferredHosts.get(service.serviceType) != service)
349         {
350           if (ph == null)
351           {
352             ph = new ArrayList<Jws2Instance>();
353           }
354           ph.add(service);
355           alternates.put(service.serviceType, ph);
356         }
357       }
358
359     }
360
361     // create GUI element for classic services
362     addEnumeratedServices(jws2al, alignFrame, enumerableServices);
363     // and the instantaneous services
364     for (final Jws2Instance service : preferredHosts.values())
365     {
366       atpoint = JvSwingUtils.findOrCreateMenu(jws2al, service.action);
367       JMenuItem hitm;
368       if (atpoint.getItemCount() > 1)
369       {
370         // previous service of this type already present
371         atpoint.addSeparator();
372       }
373       atpoint.add(hitm = new JMenuItem(service.getHost()));
374       hitm.setForeground(Color.blue);
375       hitm.addActionListener(new ActionListener()
376       {
377
378         @Override
379         public void actionPerformed(ActionEvent e)
380         {
381           Desktop.showUrl(service.getHost());
382         }
383       });
384       hitm.setToolTipText(JvSwingUtils
385               .wrapTooltip("Opens the JABAWS server's homepage in web browser"));
386
387       service.attachWSMenuEntry(atpoint, alignFrame);
388       if (alternates.containsKey(service.serviceType))
389       {
390         atpoint.add(hitm = new JMenu("Switch server"));
391         hitm.setToolTipText(JvSwingUtils
392                 .wrapTooltip("Choose a server for running this service"));
393         for (final Jws2Instance sv : alternates.get(service.serviceType))
394         {
395           JMenuItem itm;
396           hitm.add(itm = new JMenuItem(sv.getHost()));
397           itm.setForeground(Color.blue);
398           itm.addActionListener(new ActionListener()
399           {
400
401             @Override
402             public void actionPerformed(ActionEvent arg0)
403             {
404               new Thread(new Runnable()
405               {
406                 public void run()
407                 {
408                   setPreferredServiceFor(alignFrame, sv.serviceType,
409                           sv.action, sv);
410                   changeSupport.firePropertyChange("services",
411                           new Vector(), services);
412                 };
413               }).start();
414
415             }
416           });
417         }
418         /*
419          * hitm.addActionListener(new ActionListener() {
420          * 
421          * @Override public void actionPerformed(ActionEvent arg0) { new
422          * Thread(new Runnable() {
423          * 
424          * @Override public void run() { new SetPreferredServer(alignFrame,
425          * service.serviceType, service.action); } }).start(); } });
426          */
427       }
428     }
429   }
430
431   /**
432    * add services using the Java 2.5/2.6/2.7 system which optionally creates
433    * submenus to index by host and service program type
434    */
435   private void addEnumeratedServices(final JMenu jws2al,
436           final AlignFrame alignFrame, List<Jws2Instance> enumerableServices)
437   {
438     boolean byhost = Cache.getDefault("WSMENU_BYHOST", false), bytype = Cache
439             .getDefault("WSMENU_BYTYPE", false);
440     /**
441      * eventually, JWS2 services will appear under the same align/etc submenus.
442      * for moment we keep them separate.
443      */
444     JMenu atpoint;
445     MsaWSClient msacl = new MsaWSClient();
446     List<String> hostLabels = new ArrayList<String>();
447     Hashtable<String, String> lasthostFor = new Hashtable<String, String>();
448     Hashtable<String, ArrayList<Jws2Instance>> hosts = new Hashtable<String, ArrayList<Jws2Instance>>();
449     ArrayList<String> hostlist = new ArrayList<String>();
450     for (Jws2Instance service : enumerableServices)
451     {
452       ArrayList<Jws2Instance> hostservices = hosts.get(service.getHost());
453       if (hostservices == null)
454       {
455         hosts.put(service.getHost(),
456                 hostservices = new ArrayList<Jws2Instance>());
457         hostlist.add(service.getHost());
458       }
459       hostservices.add(service);
460     }
461     // now add hosts in order of the given array
462     for (String host : hostlist)
463     {
464       Jws2Instance orderedsvcs[] = hosts.get(host).toArray(
465               new Jws2Instance[1]);
466       String sortbytype[] = new String[orderedsvcs.length];
467       for (int i = 0; i < sortbytype.length; i++)
468       {
469         sortbytype[i] = orderedsvcs[i].serviceType;
470       }
471       jalview.util.QuickSort.sort(sortbytype, orderedsvcs);
472       for (final Jws2Instance service : orderedsvcs)
473       {
474         atpoint = JvSwingUtils.findOrCreateMenu(jws2al, service.action);
475         String type = service.serviceType;
476         if (byhost)
477         {
478           atpoint = JvSwingUtils.findOrCreateMenu(atpoint, host);
479           if (atpoint.getToolTipText() == null)
480           {
481             atpoint.setToolTipText("Services at " + host);
482           }
483         }
484         if (bytype)
485         {
486           atpoint = JvSwingUtils.findOrCreateMenu(atpoint, type);
487           if (atpoint.getToolTipText() == null)
488           {
489             atpoint.setToolTipText(service.getActionText());
490           }
491         }
492         if (!byhost
493                 && !hostLabels.contains(host + service.serviceType
494                         + service.getActionText()))
495         // !hostLabels.contains(host + (bytype ?
496         // service.serviceType+service.getActionText() : "")))
497         {
498           // add a marker indicating where this service is hosted
499           // relies on services from the same host being listed in a
500           // contiguous
501           // group
502           JMenuItem hitm;
503           if (hostLabels.contains(host))
504           {
505             atpoint.addSeparator();
506           }
507           else
508           {
509             hostLabels.add(host);
510           }
511           if (lasthostFor.get(service.action) == null
512                   || !lasthostFor.get(service.action).equals(host))
513           {
514             atpoint.add(hitm = new JMenuItem(host));
515             hitm.setForeground(Color.blue);
516             hitm.addActionListener(new ActionListener()
517             {
518
519               @Override
520               public void actionPerformed(ActionEvent e)
521               {
522                 Desktop.showUrl(service.getHost());
523               }
524             });
525             hitm.setToolTipText(JvSwingUtils
526                     .wrapTooltip("Opens the JABAWS server's homepage in web browser"));
527             lasthostFor.put(service.action, host);
528           }
529           hostLabels.add(host + service.serviceType
530                   + service.getActionText());
531         }
532
533         service.attachWSMenuEntry(atpoint, alignFrame);
534       }
535     }
536   }
537
538   public static void main(String[] args)
539   {
540     if (args.length > 0)
541     {
542       testUrls = new Vector<String>();
543       for (String url : args)
544       {
545         testUrls.add(url);
546       }
547       ;
548     }
549     Thread runner = getDiscoverer().startDiscoverer(
550             new PropertyChangeListener()
551             {
552
553               public void propertyChange(PropertyChangeEvent evt)
554               {
555                 if (getDiscoverer().services != null)
556                 {
557                   System.out.println("Changesupport: There are now "
558                           + getDiscoverer().services.size() + " services");
559                   int i = 1;
560                   for (Jws2Instance instance : getDiscoverer().services)
561                   {
562                     System.out.println("Service " + i++ + " "
563                             + instance.getClass() + "@"
564                             + instance.getHost() + ": "
565                             + instance.getActionText());
566                   }
567
568                 }
569               }
570             });
571     while (runner.isAlive())
572     {
573       try
574       {
575         Thread.sleep(50);
576       } catch (InterruptedException e)
577       {
578       }
579       ;
580     }
581     try
582     {
583       Thread.sleep(50);
584     } catch (InterruptedException x)
585     {
586     }
587   }
588
589   private static Jws2Discoverer discoverer;
590
591   public static Jws2Discoverer getDiscoverer()
592   {
593     if (discoverer == null)
594     {
595       discoverer = new Jws2Discoverer();
596     }
597     return discoverer;
598   }
599
600   public boolean hasServices()
601   {
602     // TODO Auto-generated method stub
603     return !running && services != null && services.size() > 0;
604   }
605
606   public boolean isRunning()
607   {
608     return running;
609   }
610
611   /**
612    * the jalview .properties entry for JWS2 URLS
613    */
614   final static String JWS2HOSTURLS = "JWS2HOSTURLS";
615
616   public static void setServiceUrls(Vector<String> urls)
617   {
618     if (urls != null)
619     {
620       StringBuffer urlbuffer = new StringBuffer();
621       String sep = "";
622       for (String url : urls)
623       {
624         urlbuffer.append(sep);
625         urlbuffer.append(url);
626         sep = ",";
627       }
628       Cache.setProperty(JWS2HOSTURLS, urlbuffer.toString());
629     }
630     else
631     {
632       Cache.removeProperty(JWS2HOSTURLS);
633     }
634   }
635
636   private static Vector<String> testUrls = null;
637
638   public static Vector<String> getServiceUrls()
639   {
640     if (testUrls != null)
641     {
642       // return test urls, if there are any, instead of touching cache
643       return testUrls;
644     }
645     String surls = Cache.getDefault(JWS2HOSTURLS,
646             "http://www.compbio.dundee.ac.uk/jabaws");
647     Vector<String> urls = new Vector<String>();
648     try
649     {
650       StringTokenizer st = new StringTokenizer(surls, ",");
651       while (st.hasMoreElements())
652       {
653         String url = null;
654         try
655         {
656           java.net.URL u = new java.net.URL(url = st.nextToken());
657           if (!urls.contains(url))
658           {
659             urls.add(url);
660           }
661           else
662           {
663             jalview.bin.Cache.log.info("Ignoring duplicate url in "
664                     + JWS2HOSTURLS + " list");
665           }
666         } catch (Exception ex)
667         {
668           jalview.bin.Cache.log
669                   .warn("Problem whilst trying to make a URL from '"
670                           + ((url != null) ? url : "<null>") + "'");
671           jalview.bin.Cache.log
672                   .warn("This was probably due to a malformed comma separated list"
673                           + " in the "
674                           + JWS2HOSTURLS
675                           + " entry of $(HOME)/.jalview_properties)");
676           jalview.bin.Cache.log.debug("Exception was ", ex);
677         }
678       }
679     } catch (Exception ex)
680     {
681       jalview.bin.Cache.log.warn(
682               "Error parsing comma separated list of urls in "
683                       + JWS2HOSTURLS + " preference.", ex);
684     }
685     if (urls.size() >= 0)
686     {
687       return urls;
688     }
689     return null;
690   }
691
692   public Vector<Jws2Instance> getServices()
693   {
694     return (services == null) ? new Vector<Jws2Instance>()
695             : new Vector<Jws2Instance>(services);
696   }
697
698   /**
699    * test the given URL with the JabaWS test code
700    * 
701    * @param foo
702    * @return
703    */
704   public static boolean testServiceUrl(URL foo)
705   {
706     try
707     {
708       compbio.ws.client.WSTester.main(new String[]
709       { "-h=" + foo.toString() });
710     } catch (Exception e)
711     {
712       e.printStackTrace();
713       return false;
714     } catch (OutOfMemoryError e)
715     {
716       e.printStackTrace();
717       return false;
718     } catch (Error e)
719     {
720       e.printStackTrace();
721       return false;
722     }
723
724     return true;
725   }
726
727   /**
728    * Start a fresh discovery thread and notify the given object when we're
729    * finished. Any known existing threads will be killed before this one is
730    * started.
731    * 
732    * @param changeSupport2
733    * @return new thread
734    */
735   public Thread startDiscoverer(PropertyChangeListener changeSupport2)
736   {
737     if (isRunning())
738     {
739       setAborted(true);
740     }
741     addPropertyChangeListener(changeSupport2);
742     Thread thr = new Thread(this);
743     thr.start();
744     return thr;
745   }
746
747   Vector<String> invalidServiceUrls = null, urlsWithoutServices = null,
748           validServiceUrls = null;
749
750   /**
751    * @return the invalidServiceUrls
752    */
753   public Vector<String> getInvalidServiceUrls()
754   {
755     return invalidServiceUrls;
756   }
757
758   /**
759    * @return the urlsWithoutServices
760    */
761   public Vector<String> getUrlsWithoutServices()
762   {
763     return urlsWithoutServices;
764   }
765
766   /**
767    * add an 'empty' JABA server to the list. Only servers not already in the
768    * 'bad URL' list will be added to this list.
769    * 
770    * @param jwsservers
771    */
772   public synchronized void addUrlwithnoservices(String jwsservers)
773   {
774     if (urlsWithoutServices == null)
775     {
776       urlsWithoutServices = new Vector<String>();
777     }
778
779     if ((invalidServiceUrls == null || !invalidServiceUrls
780             .contains(jwsservers))
781             && !urlsWithoutServices.contains(jwsservers))
782     {
783       urlsWithoutServices.add(jwsservers);
784     }
785   }
786
787   /**
788    * add a bad URL to the list
789    * 
790    * @param jwsservers
791    */
792   public synchronized void addInvalidServiceUrl(String jwsservers)
793   {
794     if (invalidServiceUrls == null)
795     {
796       invalidServiceUrls = new Vector<String>();
797     }
798     if (!invalidServiceUrls.contains(jwsservers))
799     {
800       invalidServiceUrls.add(jwsservers);
801     }
802   }
803
804   /**
805    * 
806    * @return a human readable report of any problems with the service URLs used
807    *         for discovery
808    */
809   public String getErrorMessages()
810   {
811     if (!isRunning() && !isAborted())
812     {
813       StringBuffer ermsg = new StringBuffer();
814       boolean list = false;
815       if (getInvalidServiceUrls() != null
816               && getInvalidServiceUrls().size() > 0)
817       {
818         ermsg.append("URLs that could not be contacted: \n");
819         for (String svcurl : getInvalidServiceUrls())
820         {
821           if (list)
822           {
823             ermsg.append(", ");
824           }
825           list = true;
826           ermsg.append(svcurl);
827         }
828         ermsg.append("\n\n");
829       }
830       list = false;
831       if (getUrlsWithoutServices() != null
832               && getUrlsWithoutServices().size() > 0)
833       {
834         ermsg.append("URLs without any JABA Services : \n");
835         for (String svcurl : getUrlsWithoutServices())
836         {
837           if (list)
838           {
839             ermsg.append(", ");
840           }
841           list = true;
842           ermsg.append(svcurl);
843         }
844         ermsg.append("\n");
845       }
846       if (ermsg.length() > 1)
847       {
848         return ermsg.toString();
849       }
850
851     }
852     return null;
853   }
854
855   public int getServerStatusFor(String url)
856   {
857     if (validServiceUrls != null && validServiceUrls.contains(url))
858     {
859       return 1;
860     }
861     if (urlsWithoutServices != null && urlsWithoutServices.contains(url))
862       return 0;
863     if (invalidServiceUrls != null && invalidServiceUrls.contains(url))
864     {
865       return -1;
866     }
867     return -2;
868   }
869
870   /**
871    * pick the user's preferred service based on a set of URLs (jaba server
872    * locations) and service URIs (specifying version and service interface
873    * class)
874    * 
875    * @param serviceURL
876    * @return null or best match for given uri/ls.
877    */
878   public Jws2Instance getPreferredServiceFor(String[] serviceURLs)
879   {
880     HashSet<String> urls = new HashSet<String>();
881     urls.addAll(Arrays.asList(serviceURLs));
882     Jws2Instance match = null;
883     if (services != null)
884     {
885       for (Jws2Instance svc : services)
886       {
887         if (urls.contains(svc.getServiceTypeURI()))
888         {
889           if (match == null)
890           {
891             // for moment we always pick service from server ordered first in
892             // user's preferences
893             match = svc;
894           }
895           if (urls.contains(svc.getUri()))
896           {
897             // stop and return - we've matched type URI and URI for service
898             // endpoint
899             return svc;
900           }
901         }
902       }
903     }
904     return match;
905   }
906
907   Map<String, Map<String, String>> preferredServiceMap = new HashMap<String, Map<String, String>>();;
908
909   /**
910    * get current preferred service of the given type, or global default
911    * 
912    * @param af
913    *          null or a specific alignFrame
914    * @param serviceType
915    *          Jws2Instance.serviceType for service
916    * @return null if no service of this type is available, the preferred service
917    *         for the serviceType and af if specified and if defined.
918    */
919   public Jws2Instance getPreferredServiceFor(AlignFrame af,
920           String serviceType)
921   {
922     String serviceurl = null;
923     synchronized (preferredServiceMap)
924     {
925       String afid = (af == null) ? "" : af.getViewport().getSequenceSetId();
926       Map<String, String> prefmap = preferredServiceMap.get(afid);
927       if (afid.length() > 0 && prefmap == null)
928       {
929         // recover global setting, if any
930         prefmap = preferredServiceMap.get("");
931       }
932       if (prefmap != null)
933       {
934         serviceurl = prefmap.get(serviceType);
935       }
936
937     }
938     Jws2Instance response = null;
939     for (Jws2Instance svc : services)
940     {
941       if (svc.serviceType.equals(serviceType))
942       {
943         if (serviceurl == null || serviceurl.equals(svc.getHost()))
944         {
945           response = svc;
946           break;
947         }
948       }
949     }
950     return response;
951   }
952
953   public void setPreferredServiceFor(AlignFrame af, String serviceType,
954           String serviceAction, Jws2Instance selectedServer)
955   {
956     String afid = (af == null) ? "" : af.getViewport().getSequenceSetId();
957     if (preferredServiceMap == null)
958     {
959       preferredServiceMap = new HashMap<String, Map<String, String>>();
960     }
961     Map<String, String> prefmap = preferredServiceMap.get(afid);
962     if (prefmap == null)
963     {
964       prefmap = new HashMap<String, String>();
965       preferredServiceMap.put(afid, prefmap);
966     }
967     prefmap.put(serviceType, selectedServer.getHost());
968     prefmap.put(serviceAction, selectedServer.getHost());
969   }
970
971   public void setPreferredServiceFor(String serviceType,
972           String serviceAction, Jws2Instance selectedServer)
973   {
974     setPreferredServiceFor(null, serviceType, serviceAction, selectedServer);
975   }
976 }