JAL-3878 Add annotation operations to slivka discoverer.
[jalview.git] / src / jalview / ws2 / slivka / SlivkaWSDiscoverer.java
1 package jalview.ws2.slivka;
2
3 import java.io.IOException;
4 import java.net.MalformedURLException;
5 import java.net.URL;
6 import java.util.*;
7 import java.util.concurrent.*;
8
9 import jalview.bin.Cache;
10 import jalview.ws2.*;
11 import jalview.ws2.operations.AlignmentOperation;
12 import jalview.ws2.operations.AnnotationOperation;
13 import jalview.ws2.operations.Operation;
14 import uk.ac.dundee.compbio.slivkaclient.SlivkaClient;
15 import uk.ac.dundee.compbio.slivkaclient.SlivkaService;
16
17 public class SlivkaWSDiscoverer implements WebServiceDiscoverer
18 {
19   private static final String SLIVKA_HOST_URLS = "SLIVKSHOSTURLS";
20
21   private static final String DEFAULT_URL = "https://www.compbio.dundee.ac.uk/slivka/";
22
23   private static SlivkaWSDiscoverer instance = null;
24
25   private List<WebServiceI> services = List.of();
26
27   private SlivkaWSDiscoverer()
28   {
29   }
30
31   public static SlivkaWSDiscoverer getInstance()
32   {
33     if (instance == null)
34     {
35       instance = new SlivkaWSDiscoverer();
36     }
37     return instance;
38   }
39
40   @Override
41   public List<String> getUrls()
42   {
43     String surls = Cache.getDefault(SLIVKA_HOST_URLS, DEFAULT_URL);
44     String urls[] = surls.split(",");
45     ArrayList<String> valid = new ArrayList<>(urls.length);
46     for (String url : urls)
47     {
48       try
49       {
50         new URL(url);
51         valid.add(url);
52       } catch (MalformedURLException e)
53       {
54         Cache.log.warn("Problem whilst trying to make a URL from '"
55                 + Objects.toString(url, "<null>") + "'. "
56                 + "This was probably due to malformed comma-separated-list "
57                 + "in the " + SLIVKA_HOST_URLS
58                 + " entry of ${HOME}/.jalview_properties");
59         Cache.log.debug("Exception occurred while reading url list", e);
60       }
61     }
62     return valid;
63   }
64
65   @Override
66   public void setUrls(List<String> wsUrls)
67   {
68     if (wsUrls != null && !wsUrls.isEmpty())
69     {
70       Cache.setProperty(SLIVKA_HOST_URLS, String.join(",", wsUrls));
71     }
72     else
73     {
74       Cache.removeProperty(SLIVKA_HOST_URLS);
75     }
76   }
77
78   @Override
79   public boolean testUrl(URL url)
80   {
81     return getStatusForUrl(url.toString()) == STATUS_OK;
82   }
83
84   @Override
85   public int getStatusForUrl(String url)
86   {
87     try
88     {
89       List<?> services = new SlivkaClient(url).getServices();
90       return services.isEmpty() ? STATUS_NO_SERVICES : STATUS_OK;
91     } catch (IOException e)
92     {
93       Cache.log.error("Slivka could not retrieve services list from " + url,
94               e);
95       return STATUS_INVALID;
96     }
97   }
98
99   public List<WebServiceI> getServices()
100   {
101     return Collections.unmodifiableList(services);
102   }
103
104   public boolean hasServices()
105   {
106     return !isRunning() && services.size() > 0;
107   }
108
109   public boolean isRunning()
110   {
111     for (Future<?> task : discoveryTasks)
112     {
113       if (!task.isDone())
114       {
115         return true;
116       }
117     }
118     return false;
119   }
120
121   public boolean isDone()
122   {
123     return !isRunning() && discoveryTasks.size() > 0;
124   }
125
126   private Vector<Future<?>> discoveryTasks = new Vector<>();
127
128   @Override
129   public CompletableFuture<WebServiceDiscoverer> startDiscoverer()
130   {
131     CompletableFuture<WebServiceDiscoverer> task = CompletableFuture
132             .supplyAsync(() -> {
133               reloadServices();
134               return SlivkaWSDiscoverer.this;
135             });
136     task.thenRun(() -> fireServicesChanged(getServices()));
137     discoveryTasks.add(task);
138     return task;
139   }
140
141   private List<WebServiceI> reloadServices()
142   {
143     Cache.log.info("Reloading Slivka services");
144     fireServicesChanged(Collections.emptyList());
145     ArrayList<WebServiceI> allServices = new ArrayList<>();
146     for (String url : getUrls())
147     {
148       SlivkaClient client = new SlivkaClient(url);
149       List<SlivkaService> services;
150       try
151       {
152         services = client.getServices();
153       } catch (IOException e)
154       {
155         Cache.log.error("Unable to fetch services from " + url, e);
156         continue;
157       }
158       for (SlivkaService service : services)
159       {
160         SlivkaWebService instance = new SlivkaWebService(client, service);
161         for (String classifier : service.classifiers)
162         {
163           String[] path = classifier.split("\\s*::\\s*");
164           if (path.length >= 3 && path[0].toLowerCase().equals("operation")
165                   && path[1].toLowerCase().equals("analysis"))
166           {
167             Operation op = null;
168             switch (path[path.length - 1].toLowerCase())
169             {
170             case "sequence alignment analysis (conservation)":
171               AnnotationOperation anop;
172               op = anop = new AnnotationOperation(instance,
173                   instance::getAnnotations, instance::getFeaturesFile, "Conservation");
174               anop.setAlignmentAnalysis(true);
175               anop.setInteractive(true);
176               break;
177             case "protein sequence analysis":
178               op = new AnnotationOperation(instance, instance::getAnnotations,
179                   instance::getFeaturesFile, "Protein Disorder");
180               break;
181             case "multiple sequence alignment":
182               op = new AlignmentOperation(instance, instance::getAlignment);
183               break;
184             }
185             if (op != null)
186             {
187               instance.addOperation(op);
188               break;
189             }
190           }
191         }
192         if (instance.operations.size() > 0)
193         {
194           allServices.add(instance);
195         }
196       }
197     }
198     this.services = allServices;
199     Cache.log.info("Reloading slivka services finished");
200     return allServices;
201   }
202
203   @Override
204   public String getErrorMessages()
205   {
206     return "";
207   }
208
209 }