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