JAL-972 migration from dasobert to jdas registry URLs
[jalview.git] / src / jalview / ws / dbsources / das / datamodel / DasSourceRegistry.java
1 /**
2  * 
3  */
4 package jalview.ws.dbsources.das.datamodel;
5
6 import java.net.HttpURLConnection;
7 import java.net.MalformedURLException;
8 import java.net.URL;
9 import java.util.ArrayList;
10 import java.util.Collection;
11 import java.util.Enumeration;
12 import java.util.HashMap;
13 import java.util.HashSet;
14 import java.util.Hashtable;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18 import java.util.StringTokenizer;
19 import java.util.Vector;
20
21 import javax.swing.JOptionPane;
22
23 import org.apache.http.auth.InvalidCredentialsException;
24 import org.biodas.jdas.client.ConnectionPropertyProviderI;
25 import org.biodas.jdas.client.SourcesClient;
26 import org.biodas.jdas.client.threads.MultipleConnectionPropertyProviderI;
27 import org.biodas.jdas.dassources.Capabilities;
28 import org.biodas.jdas.schema.sources.CAPABILITY;
29 import org.biodas.jdas.schema.sources.SOURCE;
30 import org.biodas.jdas.schema.sources.SOURCES;
31 import org.biodas.jdas.schema.sources.VERSION;
32
33 import jalview.bin.Cache;
34 import jalview.ws.dbsources.das.api.DasSourceRegistryI;
35 import jalview.ws.dbsources.das.api.jalviewSourceI;
36
37 /**
38  *
39  */
40 public class DasSourceRegistry implements DasSourceRegistryI,
41         MultipleConnectionPropertyProviderI
42 {
43   // private org.biodas.jdas.schema.sources.SOURCE[] dasSources = null;
44   private List<jalviewSourceI> dasSources = null;
45
46   private Hashtable<String, jalviewSourceI> sourceNames = null;
47
48   private Hashtable<String, jalviewSourceI> localSources = null;
49
50   public static String DEFAULT_REGISTRY = "http://www.dasregistry.org/das/";
51
52   /**
53    * true if thread is running and we are talking to DAS registry service
54    */
55   private boolean loadingDasSources = false;
56
57   public boolean isLoadingDasSources()
58   {
59     return loadingDasSources;
60   }
61
62   public String getDasRegistryURL()
63   {
64     String registry = jalview.bin.Cache.getDefault("DAS_REGISTRY_URL",
65             DEFAULT_REGISTRY);
66
67     if (registry.indexOf("/registry/das1/sources/") > -1)
68     {
69       jalview.bin.Cache.setProperty(jalview.bin.Cache.DAS_REGISTRY_URL,
70               DEFAULT_REGISTRY);
71       registry = DEFAULT_REGISTRY;
72     }
73     if (registry.lastIndexOf("sources.xml") == registry.length() - 11)
74     {
75       // no trailing sources.xml document for registry in JDAS
76       jalview.bin.Cache.setProperty(jalview.bin.Cache.DAS_REGISTRY_URL,
77               registry=registry.substring(0, registry.lastIndexOf("sources.xml")));
78     }
79     return registry;
80   }
81
82   /**
83    * query the default DAS Source Registry for sources. Uses value of jalview
84    * property DAS_REGISTRY_URL and the DasSourceBrowser.DEFAULT_REGISTRY if that
85    * doesn't exist.
86    * 
87    * @return list of sources
88    */
89   private List<jalviewSourceI> getDASSources()
90   {
91
92     return getDASSources(getDasRegistryURL(), this);
93   }
94
95   /**
96    * query the given URL for DasSources.
97    * 
98    * @param registryURL
99    *          return sources from registryURL
100    */
101   private static List<jalviewSourceI> getDASSources(String registryURL, MultipleConnectionPropertyProviderI registry)
102   {
103     try
104     {
105       URL url = new URL(registryURL);
106       org.biodas.jdas.client.SourcesClientInterface client = new SourcesClient();
107
108       SOURCES sources = client.fetchDataRegistry(registryURL, null, null,
109               null, null, null, null);
110
111       List<SOURCE> dassources = sources.getSOURCE();
112       ArrayList<jalviewSourceI> dsrc = new ArrayList<jalviewSourceI>();
113       HashMap<String,Integer> latests=new HashMap<String, Integer>();
114       Integer latest;
115       for (SOURCE src : dassources)
116       {
117         JalviewSource jsrc=new JalviewSource(src, registry, false);
118         latest=latests.get(jsrc.getSourceURL());
119         if (latest!=null)
120         {
121           if (jsrc.isNewerThan(dsrc.get(latest.intValue())))
122           {
123             dsrc.set(latest.intValue(), jsrc);
124           } else {
125             System.out.println("Debug: Ingored older source "+jsrc.getTitle());
126           }
127         } else {
128           latests.put(jsrc.getSourceURL(), Integer.valueOf(dsrc.size()));
129           dsrc.add(jsrc);
130         }
131       }
132       return dsrc;
133     } catch (Exception ex)
134     {
135       System.err.println("Failed to contact DAS1 registry at "
136               + registryURL);
137       ex.printStackTrace();
138       return new ArrayList<jalviewSourceI>();
139     }
140   }
141
142   public void run()
143   {
144     getSources();
145   }
146
147   @Override
148   public List<jalviewSourceI> getSources()
149   {
150     if (dasSources == null)
151     {
152       dasSources = getDASSources();
153     }
154     return appendLocalSources();
155   }
156   
157   /**
158    * generate Sources from the local das source list
159    * 
160    */
161   private void addLocalDasSources()
162   {
163     if (localSources == null)
164     {
165       // get local sources from properties and initialise the local source list
166       String local = jalview.bin.Cache.getProperty("DAS_LOCAL_SOURCE");
167       if (local != null)
168       {
169         StringTokenizer st = new StringTokenizer(local, "\t");
170         while (st.hasMoreTokens())
171         {
172           String token = st.nextToken();
173           int bar = token.indexOf("|");
174           String url = token.substring(bar + 1);
175           boolean features = true, sequence = false;
176           if (url.startsWith("sequence:"))
177           {
178             url = url.substring(9);
179             // this source also serves sequences as well as features
180             sequence = true;
181           }
182           createLocalSource(url, token.substring(0, bar), sequence,
183                   features);
184         }
185       }
186     }
187   }
188
189   private List<jalviewSourceI> appendLocalSources()
190   {
191     List<jalviewSourceI> srclist = new ArrayList<jalviewSourceI>();
192     addLocalDasSources();
193     sourceNames = new Hashtable<String, jalviewSourceI>();
194     if (dasSources != null)
195     {
196       for (jalviewSourceI src : dasSources)
197       {
198         sourceNames.put(src.getTitle(), src);
199         srclist.add(src);
200       }
201     }
202
203     if (localSources == null)
204     {
205       return srclist;
206     }
207     Enumeration en = localSources.keys();
208     while (en.hasMoreElements())
209     {
210       String key = en.nextElement().toString();
211       jalviewSourceI jvsrc = localSources.get(key);
212       sourceNames.put(key, jvsrc);
213       srclist.add(jvsrc);
214     }
215     return srclist;
216   }
217
218   /*
219  * 
220  */
221
222   @Override
223   public jalviewSourceI createLocalSource(String url, String name,
224           boolean sequence, boolean features)
225   {
226     SOURCE local = _createLocalSource(url, name, sequence, features);
227
228     if (localSources == null)
229     {
230       localSources = new Hashtable<String, jalviewSourceI>();
231     }
232     jalviewSourceI src = new JalviewSource(local, this, true);
233     localSources.put(local.getTitle(), src);
234     return src;
235   }
236
237   private SOURCE _createLocalSource(String url, String name,
238           boolean sequence, boolean features)
239   {
240     SOURCE local = new SOURCE();
241
242     local.setUri(url);
243     local.setTitle(name);
244     local.setVERSION(new ArrayList<VERSION>());
245     VERSION v = new VERSION();
246     List<CAPABILITY> cp = new ArrayList<CAPABILITY>();
247     if (sequence)
248     {
249       /*
250        * Could try and synthesize a coordinate system for the source if needbe
251        * COORDINATES coord = new COORDINATES(); coord.setAuthority("NCBI");
252        * coord.setSource("Chromosome"); coord.setTaxid("9606");
253        * coord.setVersion("35"); version.getCOORDINATES().add(coord);
254        */
255       CAPABILITY cap = new CAPABILITY();
256       cap.setType("das1:" + Capabilities.SEQUENCE.getName());
257       cap.setQueryUri(url + "/sequence");
258       cp.add(cap);
259     }
260     if (features)
261     {
262       CAPABILITY cap = new CAPABILITY();
263       cap.setType("das1:" + Capabilities.FEATURES.getName());
264       cap.setQueryUri(url + "/features");
265       cp.add(cap);
266     }
267
268     v.getCAPABILITY().addAll(cp);
269     local.getVERSION().add(v);
270
271     return local;
272   }
273
274   @Override
275   public jalviewSourceI getSource(String nickname)
276   {
277     return sourceNames.get(nickname);
278   }
279
280   @Override
281   public boolean removeLocalSource(jalviewSourceI source)
282   {
283     if (localSources.containsValue(source))
284     {
285       localSources.remove(source.getTitle());
286       sourceNames.remove(source.getTitle());
287       dasSources.remove(source);
288       jalview.bin.Cache.setProperty("DAS_LOCAL_SOURCE",
289               getLocalSourceString());
290
291       return true;
292     }
293     return false;
294   }
295
296   @Override
297   public void refreshSources()
298   {
299     dasSources = null;
300     sourceNames = null;
301     run();
302   }
303
304   @Override
305   public List<jalviewSourceI> resolveSourceNicknames(List<String> sources)
306   {
307     ArrayList<jalviewSourceI> resolved = new ArrayList<jalviewSourceI>();
308     if (sourceNames != null)
309     {
310       for (String src : sources)
311       {
312         jalviewSourceI dsrc = sourceNames.get(src);
313         if (dsrc != null)
314         {
315           resolved.add(dsrc);
316         }
317       }
318     }
319     return resolved;
320   }
321
322   @Override
323   public String getLocalSourceString()
324   {
325     if (localSources != null)
326     {
327       StringBuffer sb = new StringBuffer();
328       Enumeration en = localSources.keys();
329       while (en.hasMoreElements())
330       {
331         String token = en.nextElement().toString();
332         jalviewSourceI srco = localSources.get(token);
333         sb.append(token + "|"
334                 + (srco.isSequenceSource() ? "sequence:" : "")
335                 + srco.getUri() + "\t");
336       }
337       return sb.toString();
338     }
339     return "";
340   }
341
342   private static final Hashtable<URL, String> authStash;
343   static
344   {
345     authStash = new Hashtable<URL, String>();
346
347     try
348     {
349       // TODO: allow same credentials for https and http
350       authStash.put(new URL(
351               "http://www.compbio.dundee.ac.uk/geneweb/das/myseq/"),
352               "Basic SmltOm1pSg==");
353     } catch (MalformedURLException e)
354     {
355       // TODO Auto-generated catch block
356       e.printStackTrace();
357     }
358   }
359
360   @Override
361   public MultipleConnectionPropertyProviderI getSessionHandler()
362   {
363     return this;
364   }
365
366   @Override
367   public ConnectionPropertyProviderI getConnectionPropertyProviderFor(
368           String arg0)
369   {
370
371     final ConnectionPropertyProviderI conprov = new ConnectionPropertyProviderI()
372     {
373       boolean authed = false;
374
375       @Override
376       public void setConnectionProperties(HttpURLConnection connection)
377       {
378         String auth = authStash.get(connection.getURL());
379         if (auth != null && auth.length() > 0)
380         {
381           connection.setRequestProperty("Authorisation", auth);
382           authed = true;
383         }
384         else
385         {
386           authed = false;
387         }
388       }
389
390       @Override
391       public boolean getResponseProperties(HttpURLConnection connection)
392       {
393         String auth = authStash.get(connection.getURL());
394         if (auth != null && auth.length() == 0)
395         {
396           // don't attempt to check if we authed or not - user entered empty
397           // password
398           return false;
399         }
400         if (!authed)
401         {
402           if (auth != null)
403           {
404             // try and pass credentials.
405             return true;
406           }
407           // see if we should try and create a new auth record.
408           String ameth = connection.getHeaderField("X-DAS-AuthMethods");
409           Cache.log.debug("Could authenticate to " + connection.getURL()
410                   + " with : " + ameth);
411           // TODO: search auth string and raise login box - return if auth was
412           // provided
413           return false;
414         }
415         else
416         {
417           // check to see if auth was successful
418           String asuc = connection
419                   .getHeaderField("X-DAS_AuthenticatedUser");
420           if (asuc != null && asuc.trim().length() > 0)
421           {
422             // authentication was successful
423             Cache.log.debug("Authenticated successfully to "
424                     + connection.getURL().toString());
425             return false;
426           }
427           // it wasn't - so we should tell the user it failed and ask if they
428           // want to attempt authentication again.
429           authStash.remove(connection.getURL());
430           // open a new login/password dialog with cancel button
431           // set new authStash content with password and return true
432           return true; //
433           // User cancelled auth - so put empty string in stash to indicate we
434           // don't want to auth with this server.
435           // authStash.put(connection.getURL(), "");
436           // return false;
437         }
438       }
439     };
440     return conprov;
441   }
442
443 }