8534f062c6dfb60cc922a5082a8733f429f6d4f5
[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<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<WebServiceDiscoverer> startDiscoverer()
132   {
133     CompletableFuture<WebServiceDiscoverer> 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         SlivkaWebService instance = new SlivkaWebService(client, service);
163         Operation 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 "sequence alignment analysis (conservation)":
173               AnnotationOperation anop;
174               op = anop = new AnnotationOperation(instance,
175                   instance::getAnnotations, instance::getFeaturesFile,
176                   "Conservation");
177               anop.setAlignmentAnalysis(true);
178               anop.setInteractive(true);
179               break;
180             case "protein sequence analysis":
181               op = new AnnotationOperation(instance, instance::getAnnotations,
182                   instance::getFeaturesFile, "Protein Disorder");
183               break;
184             case "multiple sequence alignment":
185               op = new AlignmentOperation(instance, instance::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 }