JAL-3878 Add multiple sequence operation.
[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.*;
12 import uk.ac.dundee.compbio.slivkaclient.SlivkaClient;
13 import uk.ac.dundee.compbio.slivkaclient.SlivkaService;
14
15 public class SlivkaWSDiscoverer implements WebServiceDiscovererI
16 {
17   private static final String SLIVKA_HOST_URLS = "SLIVKSHOSTURLS";
18
19   private static final String DEFAULT_URL = "https://www.compbio.dundee.ac.uk/slivka/";
20
21   private static SlivkaWSDiscoverer instance = null;
22
23   private List<Operation> operations = List.of();
24
25   private SlivkaWSDiscoverer()
26   {
27   }
28
29   public static SlivkaWSDiscoverer getInstance()
30   {
31     if (instance == null)
32     {
33       instance = new SlivkaWSDiscoverer();
34     }
35     return instance;
36   }
37
38   @Override
39   public List<String> getUrls()
40   {
41     String surls = Cache.getDefault(SLIVKA_HOST_URLS, DEFAULT_URL);
42     String urls[] = surls.split(",");
43     ArrayList<String> valid = new ArrayList<>(urls.length);
44     for (String url : urls)
45     {
46       try
47       {
48         new URL(url);
49         valid.add(url);
50       } catch (MalformedURLException e)
51       {
52         Cache.log.warn("Problem whilst trying to make a URL from '"
53                 + Objects.toString(url, "<null>") + "'. "
54                 + "This was probably due to malformed comma-separated-list "
55                 + "in the " + SLIVKA_HOST_URLS
56                 + " entry of ${HOME}/.jalview_properties");
57         Cache.log.debug("Exception occurred while reading url list", e);
58       }
59     }
60     return valid;
61   }
62
63   @Override
64   public void setUrls(List<String> wsUrls)
65   {
66     if (wsUrls != null && !wsUrls.isEmpty())
67     {
68       Cache.setProperty(SLIVKA_HOST_URLS, String.join(",", wsUrls));
69     }
70     else
71     {
72       Cache.removeProperty(SLIVKA_HOST_URLS);
73     }
74   }
75
76   @Override
77   public boolean testUrl(URL url)
78   {
79     return getStatusForUrl(url.toString()) == STATUS_OK;
80   }
81
82   @Override
83   public int getStatusForUrl(String url)
84   {
85     try
86     {
87       List<?> services = new SlivkaClient(url).getServices();
88       return services.isEmpty() ? STATUS_NO_SERVICES : STATUS_OK;
89     } catch (IOException e)
90     {
91       Cache.log.error("Slivka could not retrieve services list from " + url,
92               e);
93       return STATUS_INVALID;
94     }
95   }
96
97   @Override
98   public List<Operation> getOperations()
99   {
100     return Collections.unmodifiableList(operations);
101   }
102
103   @Override
104   public boolean hasServices()
105   {
106     return !isRunning() && operations.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<WebServiceDiscovererI> startDiscoverer()
130   {
131     CompletableFuture<WebServiceDiscovererI> task = CompletableFuture
132             .supplyAsync(() -> {
133               reloadServices();
134               return SlivkaWSDiscoverer.this;
135             });
136     task.thenRun(() -> fireOperationsChanged(getOperations()));
137     discoveryTasks.add(task);
138     return task;
139   }
140
141   private List<Operation> reloadServices()
142   {
143     Cache.log.info("Reloading Slivka services");
144     fireOperationsChanged(Collections.emptyList());
145     ArrayList<Operation> allOperations= 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 webService = new SlivkaWebService(client, service);
161         AbstractOperation op = null;
162         for (String classifier : service.classifiers)
163         {
164           String[] path = classifier.split("\\s*::\\s*");
165           if (path.length >= 3 && path[0].toLowerCase().equals("operation")
166                   && path[1].toLowerCase().equals("analysis"))
167           {
168             switch (path[path.length - 1].toLowerCase())
169             {
170             case "rna secondary structure prediction":
171               op = new OperationStub(webService, "Secondary Structure Prediction");
172               op.setInteractive(true);
173               op.setAlignmentAnalysis(true);
174               op.setProteinOperation(false);
175               break;
176             case "sequence alignment analysis (conservation)":
177               op = new OperationStub(webService, "Conservation");
178               op.setAlignmentAnalysis(true);
179               op.setInteractive(true);
180               break;
181             case "protein sequence analysis":
182               op = new OperationStub(webService, "Protein Disorder");
183               break;
184             case "multiple sequence alignment":
185               op = new AlignmentOperation(webService, webService::getAlignment);
186               break;
187             }
188             if (op != null)
189             {
190               break;
191             }
192           }
193         }
194         if (op != null) {
195           allOperations.add(op);
196         }
197       }
198     }
199     this.operations = allOperations;
200     Cache.log.info("Reloading slivka services finished");
201     return allOperations;
202   }
203
204   @Override
205   public String getErrorMessages()
206   {
207     return "";
208   }
209
210 }