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