2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.6)
3 * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
5 * This file is part of Jalview.
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.
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.
16 * You should have received a copy of the GNU General Public License along with Jalview. If not, see <http://www.gnu.org/licenses/>.
18 package jalview.ws.jws2;
20 import java.awt.Color;
21 import java.awt.event.ActionEvent;
22 import java.awt.event.ActionListener;
23 import java.beans.PropertyChangeEvent;
24 import java.beans.PropertyChangeListener;
25 import java.io.Closeable;
26 import java.net.ConnectException;
28 import java.util.ArrayList;
29 import java.util.HashSet;
30 import java.util.Hashtable;
31 import java.util.List;
32 import java.util.StringTokenizer;
33 import java.util.Vector;
35 import javax.swing.JMenu;
36 import javax.swing.JMenuItem;
37 import javax.swing.event.MenuEvent;
38 import javax.swing.event.MenuListener;
40 import org.apache.log4j.Level;
42 import jalview.bin.Cache;
43 import jalview.datamodel.AlignmentView;
44 import jalview.gui.AlignFrame;
45 import jalview.gui.Desktop;
46 import jalview.gui.JalviewChangeSupport;
47 import jalview.ws.WSMenuEntryProviderI;
48 import jalview.ws.params.ParamDatastoreI;
49 import compbio.data.msa.MsaWS;
50 import compbio.metadata.Option;
51 import compbio.metadata.Preset;
52 import compbio.metadata.PresetManager;
53 import compbio.metadata.RunnerConfig;
54 import compbio.ws.client.Jws2Client;
55 import compbio.ws.client.Services;
58 * discoverer for jws2 services. Follows the lightweight service discoverer
59 * pattern (archetyped by EnfinEnvision2OneWay)
64 public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
66 private java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
70 * change listeners are notified of "services" property changes
73 * to be added that consumes new services Hashtable object.
75 public void addPropertyChangeListener(
76 java.beans.PropertyChangeListener listener)
78 changeSupport.addPropertyChangeListener(listener);
87 public void removePropertyChangeListener(
88 java.beans.PropertyChangeListener listener)
90 changeSupport.removePropertyChangeListener(listener);
93 boolean running = false, aborted = false;
98 public boolean isAborted()
107 public void setAborted(boolean aborted)
109 this.aborted = aborted;
112 Thread oldthread = null;
116 if (running && oldthread != null && oldthread.isAlive())
127 .debug("Waiting around for old discovery thread to finish.");
128 // wait around until old discoverer dies
130 } catch (Exception e)
134 Cache.log.debug("Old discovery thread has finished.");
137 oldthread = Thread.currentThread();
140 Class foo = getClass().getClassLoader().loadClass(
141 "compbio.ws.client.Jws2Client");
142 } catch (ClassNotFoundException e)
145 .println("Not enabling JABA Webservices : client jar is not available."
146 + "\nPlease check that your webstart JNLP file is up to date!");
150 // reinitialise records of good and bad service URLs
151 if (services != null)
153 services.removeAllElements();
155 if (urlsWithoutServices != null)
157 urlsWithoutServices.removeAllElements();
159 if (invalidServiceUrls != null)
161 invalidServiceUrls.removeAllElements();
164 List<JabaWsServerQuery> qrys = new ArrayList<JabaWsServerQuery>();
165 for (final String jwsservers : getServiceUrls())
167 JabaWsServerQuery squery = new JabaWsServerQuery(this, jwsservers);
169 new Thread(squery).start();
171 boolean finished = false;
177 } catch (Exception e)
181 for (JabaWsServerQuery squery : qrys)
183 finished |= !squery.isRunning();
187 Cache.log.debug("Aborting " + qrys.size()
188 + " JABAWS discovery threads.");
189 for (JabaWsServerQuery squery : qrys)
191 squery.setQuit(true);
194 } while (!aborted && !finished);
199 changeSupport.firePropertyChange("services", new Vector(), services);
204 * record this service endpoint so we can use it
210 synchronized void addService(String jwsservers, Services srv,
213 if (services == null)
215 services = new Vector<Jws2Instance>();
217 System.out.println("Discovered service: " + jwsservers + " "
219 Jws2Instance service = new Jws2Instance(jwsservers, srv.toString(),
222 services.add(service);
223 // retrieve the presets and parameter set and cache now
224 service.getParamStore().getPresets();
225 service.hasParameters();
228 public class Jws2Instance
230 public String hosturl;
232 public String serviceType;
234 public MsaWS service;
236 public Jws2Instance(String hosturl, String serviceType, MsaWS service)
239 this.hosturl = hosturl;
240 this.serviceType = serviceType;
241 this.service = service;
244 PresetManager presets = null;
246 public JabaParamStore paramStore = null;
249 * non thread safe - gets the presets for this service (blocks whilst it
250 * calls the service to get the preset set)
252 * @return service presets or null if exceptions were raised.
254 public PresetManager getPresets()
260 presets = service.getPresets();
261 } catch (Exception ex)
264 .println("Exception when retrieving presets for service "
265 + serviceType + " at " + hosturl);
271 public String getHost()
275 * try { URL serviceurl = new URL(hosturl); if (serviceurl.getPort()!=80)
276 * { return serviceurl.getHost()+":"+serviceurl.getPort(); } return
277 * serviceurl.getHost(); } catch (Exception e) {
278 * System.err.println("Failed to parse service URL '" + hosturl +
279 * "' as a valid URL!"); } return null;
284 * @return short description of what the service will do
286 public String getActionText()
288 return "Align with " + serviceType;
292 * non-thread safe - blocks whilst accessing service to get complete set of
293 * available options and parameters
297 public RunnerConfig getRunnerConfig()
299 return service.getRunnerOptions();
303 protected void finalize() throws Throwable
309 Closeable svc = (Closeable) service;
312 } catch (Exception e)
320 public ParamDatastoreI getParamStore()
322 if (paramStore == null)
326 paramStore = new JabaParamStore(this,
327 (Desktop.instance != null ? Desktop
328 .getUserParameterStore() : null));
329 } catch (Exception ex)
337 public String getUri()
339 // this is only valid for Jaba 1.0 - this formula might have to change!
341 + (hosturl.lastIndexOf("/") == (hosturl.length() - 1) ? "/"
345 private boolean hasParams = false, lookedForParams = false;
347 public boolean hasParameters()
349 if (!lookedForParams)
351 lookedForParams = true;
354 hasParams = (getRunnerConfig().getArguments().size() > 0);
355 } catch (Exception e)
365 * holds list of services.
367 protected Vector<Jws2Instance> services;
370 * find or add a submenu with the given title in the given menu
374 * @return the new or existing submenu
376 private JMenu findOrCreateMenu(JMenu menu, String submenu)
378 JMenu submenuinstance = null;
379 for (int i = 0, iSize = menu.getMenuComponentCount(); i < iSize; i++)
381 if (menu.getMenuComponent(i) instanceof JMenu
382 && ((JMenu) menu.getMenuComponent(i)).getText().equals(
385 submenuinstance = (JMenu) menu.getMenuComponent(i);
388 if (submenuinstance == null)
390 submenuinstance = new JMenu(submenu);
391 menu.add(submenuinstance);
393 return submenuinstance;
397 public void attachWSMenuEntry(JMenu wsmenu, final AlignFrame alignFrame)
399 // dynamically regenerate service list.
400 final JMenu jws2al = new JMenu("JABAWS Alignment");
401 jws2al.addMenuListener(new MenuListener()
403 // TODO: future: add menu listener to parent menu - so submenus are
404 // populated *before* they are selected.
406 public void menuSelected(MenuEvent e)
408 populateWSMenuEntry(jws2al, alignFrame);
412 public void menuDeselected(MenuEvent e)
414 // TODO Auto-generated method stub
419 public void menuCanceled(MenuEvent e)
421 // TODO Auto-generated method stub
430 private void populateWSMenuEntry(JMenu jws2al, final AlignFrame alignFrame)
432 if (running || services == null || services.size() == 0)
436 boolean byhost = Cache.getDefault("WSMENU_BYHOST", true), bytype = Cache
437 .getDefault("WSMENU_BYTYPE", true);
439 * eventually, JWS2 services will appear under the same align/etc submenus.
440 * for moment we keep them separate.
443 MsaWSClient msacl = new MsaWSClient();
444 Vector hostLabels = new Vector();
446 String lasthost = null;
447 for (final Jws2Instance service : services)
450 String host = service.getHost();
451 String type = service.serviceType;
454 atpoint = findOrCreateMenu(atpoint, host);
455 if (atpoint.getToolTipText() == null)
457 atpoint.setToolTipText("Services at " + host);
462 atpoint = findOrCreateMenu(atpoint, type);
463 if (atpoint.getToolTipText() == null)
465 atpoint.setToolTipText(service.getActionText());
469 && !hostLabels.contains(host + service.serviceType
470 + service.getActionText()))
471 // !hostLabels.contains(host + (bytype ?
472 // service.serviceType+service.getActionText() : "")))
474 // add a marker indicating where this service is hosted
475 // relies on services from the same host being listed in a contiguous
478 atpoint.addSeparator();
479 if (lasthost == null || !lasthost.equals(host))
481 atpoint.add(hitm = new JMenuItem(host));
482 hitm.setForeground(Color.blue);
485 hostLabels.addElement(host + service.serviceType
486 + service.getActionText());
487 // hostLabels.addElement(host + (bytype ?
488 // service.serviceType+service.getActionText() : ""));
490 msacl.attachWSMenuEntry(atpoint, service, alignFrame);
492 * JMenuItem sitem = new JMenuItem(service.serviceType);
493 * sitem.setToolTipText("Hosted at " + service.hosturl);
494 * sitem.addActionListener(new ActionListener() {
496 * @Override public void actionPerformed(ActionEvent e) { AlignmentView
497 * msa = alignFrame.gatherSequencesForAlignment(); MsaWSClient client =
498 * new MsaWSClient(service, "JWS2 Alignment of " + alignFrame.getTitle(),
499 * msa, false, true, alignFrame.getViewport().getAlignment().getDataset(),
505 public static void main(String[] args)
507 Thread runner = getDiscoverer().startDiscoverer(
508 new PropertyChangeListener()
511 public void propertyChange(PropertyChangeEvent evt)
513 if (getDiscoverer().services != null)
515 System.out.println("Changesupport: There are now "
516 + getDiscoverer().services.size() + " services");
521 while (runner.isAlive())
526 } catch (InterruptedException e)
533 private static Jws2Discoverer discoverer;
535 public static Jws2Discoverer getDiscoverer()
537 if (discoverer == null)
539 discoverer = new Jws2Discoverer();
544 public boolean hasServices()
546 // TODO Auto-generated method stub
547 return !running && services != null && services.size() > 0;
550 public boolean isRunning()
556 * the jalview .properties entry for JWS2 URLS
558 final static String JWS2HOSTURLS = "JWS2HOSTURLS";
560 public static void setServiceUrls(Vector<String> urls)
564 StringBuffer urlbuffer = new StringBuffer();
566 for (String url : urls)
568 urlbuffer.append(sep);
569 urlbuffer.append(url);
572 Cache.setProperty(JWS2HOSTURLS, urlbuffer.toString());
576 Cache.removeProperty(JWS2HOSTURLS);
580 public static Vector<String> getServiceUrls()
582 String surls = Cache.getDefault(JWS2HOSTURLS,
583 "http://www.compbio.dundee.ac.uk/jabaws");
584 Vector<String> urls = new Vector<String>();
587 StringTokenizer st = new StringTokenizer(surls, ",");
588 while (st.hasMoreElements())
593 java.net.URL u = new java.net.URL(url = st.nextToken());
594 if (!urls.contains(url))
600 jalview.bin.Cache.log.info("Ignoring duplicate url in "
601 + JWS2HOSTURLS + " list");
603 } catch (Exception ex)
605 jalview.bin.Cache.log
606 .warn("Problem whilst trying to make a URL from '"
607 + ((url != null) ? url : "<null>") + "'");
608 jalview.bin.Cache.log
609 .warn("This was probably due to a malformed comma separated list"
612 + " entry of $(HOME)/.jalview_properties)");
613 jalview.bin.Cache.log.debug("Exception was ", ex);
616 } catch (Exception ex)
618 jalview.bin.Cache.log.warn(
619 "Error parsing comma separated list of urls in "
620 + JWS2HOSTURLS + " preference.", ex);
622 if (urls.size() >= 0)
629 public Vector<Jws2Instance> getServices()
631 return (services == null) ? new Vector<Jws2Instance>()
632 : new Vector<Jws2Instance>(services);
636 * test the given URL with the JabaWS test code
641 public static boolean testServiceUrl(URL foo)
645 compbio.ws.client.WSTester.main(new String[]
646 { "-h=" + foo.toString() });
647 } catch (Exception e)
651 } catch (OutOfMemoryError e)
665 * Start a fresh discovery thread and notify the given object when we're
666 * finished. Any known existing threads will be killed before this one is
669 * @param changeSupport2
672 public Thread startDiscoverer(PropertyChangeListener changeSupport2)
678 addPropertyChangeListener(changeSupport2);
679 Thread thr = new Thread(this);
684 Vector<String> invalidServiceUrls = null, urlsWithoutServices = null;
687 * @return the invalidServiceUrls
689 public Vector<String> getInvalidServiceUrls()
691 return invalidServiceUrls;
695 * @return the urlsWithoutServices
697 public Vector<String> getUrlsWithoutServices()
699 return urlsWithoutServices;
703 * add an 'empty' JABA server to the list. Only servers not already in the 'bad URL' list will be added to this list.
707 public synchronized void addUrlwithnoservices(String jwsservers)
709 if (urlsWithoutServices == null)
711 urlsWithoutServices = new Vector<String>();
714 if ((invalidServiceUrls == null || !invalidServiceUrls
715 .contains(jwsservers))
716 && !urlsWithoutServices.contains(jwsservers))
718 urlsWithoutServices.add(jwsservers);
723 * add a bad URL to the list
727 public synchronized void addInvalidServiceUrl(String jwsservers)
729 if (invalidServiceUrls == null)
731 invalidServiceUrls = new Vector<String>();
733 if (!invalidServiceUrls.contains(jwsservers))
735 invalidServiceUrls.add(jwsservers);
741 * @return a human readable report of any problems with the service URLs used
744 public String getErrorMessages()
746 if (!isRunning() && !isAborted())
748 StringBuffer ermsg = new StringBuffer();
749 boolean list = false;
750 if (getInvalidServiceUrls() != null
751 && getInvalidServiceUrls().size() > 0)
753 ermsg.append("URLs that could not be contacted: \n");
754 for (String svcurl : getInvalidServiceUrls())
761 ermsg.append(svcurl);
763 ermsg.append("\n\n");
766 if (getUrlsWithoutServices() != null
767 && getUrlsWithoutServices().size() > 0)
769 ermsg.append("URLs without any JABA Services : \n");
770 for (String svcurl : getUrlsWithoutServices())
777 ermsg.append(svcurl);
781 if (ermsg.length() > 1)
783 return ermsg.toString();