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