JAL-3032 Dialogs Color picker -- untested commit
[jalview.git] / src / jalview / ws / dbsources / das / datamodel / DasSourceRegistry.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ 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
50         implements DasSourceRegistryI, 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   @Override
73   public String getDasRegistryURL()
74   {
75     String registry = jalview.bin.Cache.getDefault("DAS_REGISTRY_URL",
76             DEFAULT_REGISTRY);
77
78     if (registry.indexOf("/registry/das1/sources/") > -1)
79     {
80       jalview.bin.Cache.setProperty(jalview.bin.Cache.DAS_REGISTRY_URL,
81               DEFAULT_REGISTRY);
82       registry = DEFAULT_REGISTRY;
83     }
84     if (registry.lastIndexOf("sources.xml") == registry.length() - 11)
85     {
86       // no trailing sources.xml document for registry in JDAS
87       jalview.bin.Cache.setProperty(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(
141                     "Debug: Ignored older source " + 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.out.println(
154               "DAS1 registry at " + registryURL + " no longer exists");
155       return new ArrayList<jalviewSourceI>();
156     }
157   }
158
159   public void run()
160   {
161     getSources();
162   }
163
164   @Override
165   public List<jalviewSourceI> getSources()
166   {
167     if (dasSources == null)
168     {
169       dasSources = getDASSources();
170     }
171     return appendLocalSources();
172   }
173
174   /**
175    * generate Sources from the local das source list
176    * 
177    */
178   private void addLocalDasSources()
179   {
180     if (localSources == null)
181     {
182       // get local sources from properties and initialise the local source list
183       String local = jalview.bin.Cache.getProperty("DAS_LOCAL_SOURCE");
184
185       if (local != null)
186       {
187         int n = 1;
188         StringTokenizer st = new StringTokenizer(local, "\t");
189         while (st.hasMoreTokens())
190         {
191           String token = st.nextToken();
192           int bar = token.indexOf("|");
193           if (bar == -1)
194           {
195             System.err.println(
196                     "Warning: DAS user local source appears to have no nickname (expected a '|' followed by nickname)\nOffending definition: '"
197                             + token + "'");
198           }
199           String url = token.substring(bar + 1);
200           boolean features = true, sequence = false;
201           if (url.startsWith("sequence:"))
202           {
203             url = url.substring(9);
204             // this source also serves sequences as well as features
205             sequence = true;
206           }
207           try
208           {
209             if (bar > -1)
210             {
211               createLocalSource(url, token.substring(0, bar), sequence,
212                       features);
213             }
214             else
215             {
216               createLocalSource(url, "User Source" + n, sequence, features);
217             }
218           } catch (Exception q)
219           {
220             System.err.println(
221                     "Unexpected exception when creating local source from '"
222                             + token + "'");
223             q.printStackTrace();
224           }
225           n++;
226         }
227       }
228     }
229   }
230
231   private List<jalviewSourceI> appendLocalSources()
232   {
233     List<jalviewSourceI> srclist = new ArrayList<jalviewSourceI>();
234     addLocalDasSources();
235     sourceNames = new Hashtable<String, jalviewSourceI>();
236     if (dasSources != null)
237     {
238       for (jalviewSourceI src : dasSources)
239       {
240         sourceNames.put(src.getTitle(), src);
241         srclist.add(src);
242       }
243     }
244
245     if (localSources == null)
246     {
247       return srclist;
248     }
249     Enumeration en = localSources.keys();
250     while (en.hasMoreElements())
251     {
252       String key = en.nextElement().toString();
253       jalviewSourceI jvsrc = localSources.get(key);
254       sourceNames.put(key, jvsrc);
255       srclist.add(jvsrc);
256     }
257     return srclist;
258   }
259
260   /*
261   * 
262   */
263
264   @Override
265   public jalviewSourceI createLocalSource(String url, String name,
266           boolean sequence, boolean features)
267   {
268     SOURCE local = _createLocalSource(url, name, sequence, features);
269
270     if (localSources == null)
271     {
272       localSources = new Hashtable<String, jalviewSourceI>();
273     }
274     jalviewSourceI src = new JalviewSource(local, this, true);
275     localSources.put(local.getTitle(), src);
276     return src;
277   }
278
279   private SOURCE _createLocalSource(String url, String name,
280           boolean sequence, boolean features)
281   {
282     SOURCE local = new SOURCE();
283
284     local.setUri(url);
285     local.setTitle(name);
286     local.setVERSION(new ArrayList<VERSION>());
287     VERSION v = new VERSION();
288     List<CAPABILITY> cp = new ArrayList<CAPABILITY>();
289     if (sequence)
290     {
291       /*
292        * Could try and synthesize a coordinate system for the source if needbe
293        * COORDINATES coord = new COORDINATES(); coord.setAuthority("NCBI");
294        * coord.setSource("Chromosome"); coord.setTaxid("9606");
295        * coord.setVersion("35"); version.getCOORDINATES().add(coord);
296        */
297       CAPABILITY cap = new CAPABILITY();
298       cap.setType("das1:" + Capabilities.SEQUENCE.getName());
299       cap.setQueryUri(url + "/sequence");
300       cp.add(cap);
301     }
302     if (features)
303     {
304       CAPABILITY cap = new CAPABILITY();
305       cap.setType("das1:" + Capabilities.FEATURES.getName());
306       cap.setQueryUri(url + "/features");
307       cp.add(cap);
308     }
309
310     v.getCAPABILITY().addAll(cp);
311     local.getVERSION().add(v);
312
313     return local;
314   }
315
316   @Override
317   public jalviewSourceI getSource(String nickname)
318   {
319     return sourceNames.get(nickname);
320   }
321
322   @Override
323   public boolean removeLocalSource(jalviewSourceI source)
324   {
325     if (localSources.containsValue(source))
326     {
327       localSources.remove(source.getTitle());
328       sourceNames.remove(source.getTitle());
329       dasSources.remove(source);
330       jalview.bin.Cache.setProperty("DAS_LOCAL_SOURCE",
331               getLocalSourceString());
332
333       return true;
334     }
335     return false;
336   }
337
338   @Override
339   public void refreshSources()
340   {
341     dasSources = null;
342     sourceNames = null;
343     run();
344   }
345
346   @Override
347   public List<jalviewSourceI> resolveSourceNicknames(List<String> sources)
348   {
349     ArrayList<jalviewSourceI> resolved = new ArrayList<jalviewSourceI>();
350     if (sourceNames != null)
351     {
352       for (String src : sources)
353       {
354         jalviewSourceI dsrc = sourceNames.get(src);
355         if (dsrc != null)
356         {
357           resolved.add(dsrc);
358         }
359       }
360     }
361     return resolved;
362   }
363
364   @Override
365   public String getLocalSourceString()
366   {
367     if (localSources != null)
368     {
369       StringBuffer sb = new StringBuffer();
370       Enumeration en = localSources.keys();
371       while (en.hasMoreElements())
372       {
373         String token = en.nextElement().toString();
374         jalviewSourceI srco = localSources.get(token);
375         sb.append(token + "|" + (srco.isSequenceSource() ? "sequence:" : "")
376                 + srco.getUri() + "\t");
377       }
378       return sb.toString();
379     }
380     return "";
381   }
382
383   private static final Hashtable<URL, String> authStash;
384   static
385   {
386     authStash = new Hashtable<URL, String>();
387
388     try
389     {
390       // TODO: allow same credentials for https and http
391       authStash.put(
392               new URL("http://www.compbio.dundee.ac.uk/geneweb/das/myseq/"),
393               "Basic SmltOm1pSg==");
394     } catch (MalformedURLException e)
395     {
396       // TODO Auto-generated catch block
397       e.printStackTrace();
398     }
399   }
400
401   @Override
402   public MultipleConnectionPropertyProviderI getSessionHandler()
403   {
404     return this;
405   }
406
407   @Override
408   public ConnectionPropertyProviderI getConnectionPropertyProviderFor(
409           String arg0)
410   {
411
412     final ConnectionPropertyProviderI conprov = new ConnectionPropertyProviderI()
413     {
414       boolean authed = false;
415
416       @Override
417       public void setConnectionProperties(HttpURLConnection connection)
418       {
419         String auth = authStash.get(connection.getURL());
420         if (auth != null && auth.length() > 0)
421         {
422           connection.setRequestProperty("Authorisation", auth);
423           authed = true;
424         }
425         else
426         {
427           authed = false;
428         }
429       }
430
431       @Override
432       public boolean getResponseProperties(HttpURLConnection connection)
433       {
434         String auth = authStash.get(connection.getURL());
435         if (auth != null && auth.length() == 0)
436         {
437           // don't attempt to check if we authed or not - user entered empty
438           // password
439           return false;
440         }
441         if (!authed)
442         {
443           if (auth != null)
444           {
445             // try and pass credentials.
446             return true;
447           }
448           // see if we should try and create a new auth record.
449           String ameth = connection.getHeaderField("X-DAS-AuthMethods");
450           Cache.log.debug("Could authenticate to " + connection.getURL()
451                   + " with : " + ameth);
452           // TODO: search auth string and raise login box - return if auth was
453           // provided
454           return false;
455         }
456         else
457         {
458           // check to see if auth was successful
459           String asuc = connection
460                   .getHeaderField("X-DAS_AuthenticatedUser");
461           if (asuc != null && asuc.trim().length() > 0)
462           {
463             // authentication was successful
464             Cache.log.debug("Authenticated successfully to "
465                     + connection.getURL().toString());
466             return false;
467           }
468           // it wasn't - so we should tell the user it failed and ask if they
469           // want to attempt authentication again.
470           authStash.remove(connection.getURL());
471           // open a new login/password dialog with cancel button
472           // set new authStash content with password and return true
473           return true; //
474           // User cancelled auth - so put empty string in stash to indicate we
475           // don't want to auth with this server.
476           // authStash.put(connection.getURL(), "");
477           // return false;
478         }
479       }
480     };
481     return conprov;
482   }
483
484 }