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