JAL-3878 Add annotation operation and worker to the services.
[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 AnnotationOperation(webService,
172                   "Secondary Structure Prediction", webService::attachAnnotations);
173               op.setInteractive(true);
174               op.setAlignmentAnalysis(true);
175               op.setProteinOperation(false);
176               break;
177             case "sequence alignment analysis (conservation)":
178               op = new AnnotationOperation(webService, "Conservation",
179                   webService::attachAnnotations);
180               op.setAlignmentAnalysis(true);
181               op.setInteractive(true);
182               break;
183             case "protein sequence analysis":
184               op = new AnnotationOperation(webService, "Protein Disorder",
185                   webService::attachAnnotations);
186               break;
187             case "multiple sequence alignment":
188               op = new AlignmentOperation(webService, webService::getAlignment);
189               break;
190             }
191             if (op != null)
192             {
193               break;
194             }
195           }
196         }
197         if (op != null)
198         {
199           allOperations.add(op);
200         }
201       }
202     }
203     this.operations = allOperations;
204     Cache.log.info("Reloading slivka services finished");
205     return allOperations;
206   }
207
208   @Override
209   public String getErrorMessages()
210   {
211     return "";
212   }
213
214 }