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