JAL-3878 Fix typo in slivka host urls variable name.
[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.datamodel.AlignmentI;
11 import jalview.io.JPredFile;
12 import jalview.ws2.*;
13 import jalview.ws2.operations.*;
14 import uk.ac.dundee.compbio.slivkaclient.SlivkaClient;
15 import uk.ac.dundee.compbio.slivkaclient.SlivkaService;
16
17 public class SlivkaWSDiscoverer implements WebServiceDiscovererI
18 {
19   private static final String SLIVKA_HOST_URLS = "SLIVKAHOSTURLS";
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<Operation> operations = 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   @Override
100   public List<Operation> getOperations()
101   {
102     return Collections.unmodifiableList(operations);
103   }
104
105   @Override
106   public boolean hasServices()
107   {
108     return !isRunning() && operations.size() > 0;
109   }
110
111   public boolean isRunning()
112   {
113     for (Future<?> task : discoveryTasks)
114     {
115       if (!task.isDone())
116       {
117         return true;
118       }
119     }
120     return false;
121   }
122
123   public boolean isDone()
124   {
125     return !isRunning() && discoveryTasks.size() > 0;
126   }
127
128   private Vector<Future<?>> discoveryTasks = new Vector<>();
129
130   @Override
131   public CompletableFuture<WebServiceDiscovererI> startDiscoverer()
132   {
133     CompletableFuture<WebServiceDiscovererI> task = CompletableFuture
134         .supplyAsync(() -> {
135           reloadServices();
136           return SlivkaWSDiscoverer.this;
137         });
138     task.thenRun(() -> fireOperationsChanged(getOperations()));
139     discoveryTasks.add(task);
140     return task;
141   }
142
143   private List<Operation> reloadServices()
144   {
145     Cache.log.info("Reloading Slivka services");
146     fireOperationsChanged(Collections.emptyList());
147     ArrayList<Operation> allOperations = new ArrayList<>();
148     for (String url : getUrls())
149     {
150       SlivkaClient client = new SlivkaClient(url);
151       List<SlivkaService> services;
152       try
153       {
154         services = client.getServices();
155       } catch (IOException e)
156       {
157         Cache.log.error("Unable to fetch services from " + url, e);
158         continue;
159       }
160       for (SlivkaService service : services)
161       {
162         final SlivkaWebService webService = new SlivkaWebService(client, service);
163         AbstractOperation op = null;
164         for (String classifier : service.classifiers)
165         {
166           String[] path = classifier.split("\\s*::\\s*");
167           if (path.length >= 3 && path[0].toLowerCase().equals("operation")
168               && path[1].toLowerCase().equals("analysis"))
169           {
170             switch (path[path.length - 1].toLowerCase())
171             {
172             case "rna secondary structure prediction":
173               op = new AnnotationOperation(webService,
174                   "Secondary Structure Prediction", webService::attachAnnotations);
175               op.setInteractive(true);
176               op.setAlignmentAnalysis(true);
177               op.setProteinOperation(false);
178               break;
179             case "sequence alignment analysis (conservation)":
180               op = new AnnotationOperation(webService, "Conservation",
181                   webService::attachAnnotations);
182               op.setAlignmentAnalysis(true);
183               op.setInteractive(true);
184               break;
185             case "protein sequence analysis":
186               op = new AnnotationOperation(webService, "Protein Disorder",
187                   webService::attachAnnotations);
188               break;
189             case "protein secondary structure prediction":
190               var predictionSupplier = new JPredOperation.PredictionResultSupplier()
191               {
192                 @Override
193                 public JPredFile getPrediction(WSJob job) throws IOException
194                 {
195                   return webService.getPrediction(job);
196                 }
197
198                 @Override
199                 public AlignmentI getAlignment(WSJob job) throws IOException
200                 {
201                   return webService.getAlignment(job);
202                 }
203               };
204               op = new JPredOperation(webService,
205                   "Secondary Structure Prediction", predictionSupplier);
206               break;
207             case "multiple sequence alignment":
208               op = new AlignmentOperation(webService, webService::getAlignment);
209               break;
210             }
211             if (op != null)
212             {
213               break;
214             }
215           }
216         }
217         if (op != null)
218         {
219           allOperations.add(op);
220         }
221       }
222     }
223     this.operations = allOperations;
224     Cache.log.info("Reloading slivka services finished");
225     return allOperations;
226   }
227
228   @Override
229   public String getErrorMessages()
230   {
231     return "";
232   }
233
234   
235   private OperationsChangeListenerList operationsChangeListeners =
236           new OperationsChangeListenerList(this);
237   
238   @Override
239   public void addOperationsChangeListener(OperationsChangeListener listener)
240   {
241     operationsChangeListeners.addListener(listener);
242   }
243
244   @Override
245   public void removeOperationsChangeListener(
246           OperationsChangeListener listener)
247   {
248      operationsChangeListeners.removeListener(listener);
249   }
250   
251   private void fireOperationsChanged(List<Operation> list)
252   {
253     operationsChangeListeners.fireOperationsChanged(list);
254   }
255
256 }