Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
EnsemblRestClient | 48 | 144 | 56 |
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.ext.ensembl; | |
22 | ||
23 | import java.io.BufferedReader; | |
24 | import java.io.DataOutputStream; | |
25 | import java.io.IOException; | |
26 | import java.io.InputStream; | |
27 | import java.net.HttpURLConnection; | |
28 | import java.net.MalformedURLException; | |
29 | import java.net.ProtocolException; | |
30 | import java.net.URL; | |
31 | import java.util.HashMap; | |
32 | import java.util.List; | |
33 | import java.util.Map; | |
34 | ||
35 | import javax.ws.rs.HttpMethod; | |
36 | ||
37 | import org.json.simple.parser.ParseException; | |
38 | ||
39 | import jalview.util.HttpUtils; | |
40 | import jalview.util.Platform; | |
41 | import jalview.util.StringUtils; | |
42 | ||
43 | /** | |
44 | * Base class for Ensembl REST service clients | |
45 | * | |
46 | * @author gmcarstairs | |
47 | */ | |
48 | abstract class EnsemblRestClient extends EnsemblSequenceFetcher | |
49 | { | |
50 | ||
51 | 7 | static |
52 | { | |
53 | 7 | Platform.addJ2SDirectDatabaseCall("http://rest.ensembl"); |
54 | 7 | Platform.addJ2SDirectDatabaseCall("https://rest.ensembl"); |
55 | } | |
56 | ||
57 | private static final int DEFAULT_READ_TIMEOUT = 5 * 60 * 1000; // 5 minutes | |
58 | ||
59 | private static final int CONNECT_TIMEOUT_MS = 10 * 1000; // 10 seconds | |
60 | ||
61 | private static final int MAX_RETRIES = 3; | |
62 | ||
63 | private static final int HTTP_OK = 200; | |
64 | ||
65 | private static final int HTTP_OVERLOAD = 429; | |
66 | ||
67 | /* | |
68 | * update these constants when Jalview has been checked / updated for | |
69 | * changes to Ensembl REST API, and updated JAL-3018 | |
70 | * @see https://github.com/Ensembl/ensembl-rest/wiki/Change-log | |
71 | * @see http://rest.ensembl.org/info/rest?content-type=application/json | |
72 | */ | |
73 | private static final String LATEST_ENSEMBLGENOMES_REST_VERSION = "15.8"; | |
74 | ||
75 | private static final String LATEST_ENSEMBL_REST_VERSION = "15.8"; | |
76 | ||
77 | private static final String REST_CHANGE_LOG = "https://github.com/Ensembl/ensembl-rest/wiki/Change-log"; | |
78 | ||
79 | private static Map<String, EnsemblData> domainData; | |
80 | ||
81 | private final static long AVAILABILITY_RETEST_INTERVAL = 10000L; // 10 seconds | |
82 | ||
83 | private final static long VERSION_RETEST_INTERVAL = 1000L * 3600; // 1 hr | |
84 | ||
85 | protected static final String CONTENT_TYPE_JSON = "?content-type=application/json"; | |
86 | ||
87 | 7 | static |
88 | { | |
89 | 7 | domainData = new HashMap<>(); |
90 | 7 | domainData.put(DEFAULT_ENSEMBL_BASEURL, new EnsemblData( |
91 | DEFAULT_ENSEMBL_BASEURL, LATEST_ENSEMBL_REST_VERSION)); | |
92 | 7 | domainData.put(DEFAULT_ENSEMBL_GENOMES_BASEURL, |
93 | new EnsemblData(DEFAULT_ENSEMBL_GENOMES_BASEURL, | |
94 | LATEST_ENSEMBLGENOMES_REST_VERSION)); | |
95 | } | |
96 | ||
97 | protected volatile boolean inProgress = false; | |
98 | ||
99 | /** | |
100 | * Default constructor to use rest.ensembl.org | |
101 | */ | |
102 | 49 | public EnsemblRestClient() |
103 | { | |
104 | 49 | super(); |
105 | ||
106 | /* | |
107 | * initialise domain info lazily | |
108 | */ | |
109 | 49 | if (!domainData.containsKey(ensemblDomain)) |
110 | { | |
111 | 0 | domainData.put(ensemblDomain, |
112 | new EnsemblData(ensemblDomain, LATEST_ENSEMBL_REST_VERSION)); | |
113 | } | |
114 | 49 | if (!domainData.containsKey(ensemblGenomesDomain)) |
115 | { | |
116 | 0 | domainData.put(ensemblGenomesDomain, new EnsemblData( |
117 | ensemblGenomesDomain, LATEST_ENSEMBLGENOMES_REST_VERSION)); | |
118 | } | |
119 | } | |
120 | ||
121 | /** | |
122 | * Constructor given the target domain to fetch data from | |
123 | * | |
124 | * @param d | |
125 | */ | |
126 | 1 | public EnsemblRestClient(String d) |
127 | { | |
128 | 1 | setDomain(d); |
129 | } | |
130 | ||
131 | 0 | @Override |
132 | public boolean queryInProgress() | |
133 | { | |
134 | 0 | return inProgress; |
135 | } | |
136 | ||
137 | 0 | @Override |
138 | public StringBuffer getRawRecords() | |
139 | { | |
140 | 0 | return null; |
141 | } | |
142 | ||
143 | /** | |
144 | * Returns the URL for the client http request | |
145 | * | |
146 | * @param ids | |
147 | * @return | |
148 | * @throws MalformedURLException | |
149 | */ | |
150 | protected abstract URL getUrl(List<String> ids) | |
151 | throws MalformedURLException; | |
152 | ||
153 | /** | |
154 | * Returns true if client uses GET method, false if it uses POST | |
155 | * | |
156 | * @return | |
157 | */ | |
158 | protected abstract boolean useGetRequest(); | |
159 | ||
160 | /** | |
161 | * Returns the desired value for the Content-Type request header. Default is | |
162 | * application/json, override if required to vary this. | |
163 | * | |
164 | * @return | |
165 | * @see https://github.com/Ensembl/ensembl-rest/wiki/HTTP-Headers | |
166 | */ | |
167 | 0 | protected String getRequestMimeType() |
168 | { | |
169 | 0 | return "application/json"; |
170 | } | |
171 | ||
172 | /** | |
173 | * Return the desired value for the Accept request header. Default is | |
174 | * application/json, override if required to vary this. | |
175 | * | |
176 | * @return | |
177 | * @see https://github.com/Ensembl/ensembl-rest/wiki/HTTP-Headers | |
178 | */ | |
179 | 0 | protected String getResponseMimeType() |
180 | { | |
181 | 0 | return "application/json"; |
182 | } | |
183 | ||
184 | /** | |
185 | * Checks Ensembl's REST 'ping' endpoint, and returns true if response | |
186 | * indicates available, else false | |
187 | * | |
188 | * @see http://rest.ensembl.org/documentation/info/ping | |
189 | * @return | |
190 | */ | |
191 | 0 | @SuppressWarnings("unchecked") |
192 | boolean checkEnsembl() | |
193 | { | |
194 | 0 | BufferedReader br = null; |
195 | 0 | String pingUrl = getDomain() + "/info/ping" + CONTENT_TYPE_JSON; |
196 | 0 | try |
197 | { | |
198 | // note this format works for both ensembl and ensemblgenomes | |
199 | // info/ping.json works for ensembl only (March 2016) | |
200 | ||
201 | /* | |
202 | * expect {"ping":1} if ok | |
203 | * if ping takes more than 2 seconds to respond, treat as if unavailable | |
204 | */ | |
205 | 0 | Map<String, Object> val = (Map<String, Object>) getJSON( |
206 | new URL(pingUrl), null, 2 * 1000, MODE_MAP, null); | |
207 | 0 | if (val == null) |
208 | { | |
209 | 0 | return false; |
210 | } | |
211 | 0 | String pingString = val.get("ping").toString(); |
212 | 0 | return pingString != null; |
213 | } catch (Throwable t) | |
214 | { | |
215 | 0 | jalview.bin.Console.errPrintln( |
216 | "Error connecting to " + pingUrl + ": " + t.getMessage()); | |
217 | } finally | |
218 | { | |
219 | 0 | if (br != null) |
220 | { | |
221 | 0 | try |
222 | { | |
223 | 0 | br.close(); |
224 | } catch (IOException e) | |
225 | { | |
226 | // ignore | |
227 | } | |
228 | } | |
229 | } | |
230 | 0 | return false; |
231 | } | |
232 | ||
233 | protected final static int MODE_ARRAY = 0; | |
234 | ||
235 | protected final static int MODE_MAP = 1; | |
236 | ||
237 | protected final static int MODE_ITERATOR = 2; | |
238 | ||
239 | // /** | |
240 | // * Returns a reader to a (Json) response from the Ensembl sequence endpoint. | |
241 | // * If the request failed the return value may be null. | |
242 | // * | |
243 | // * @param ids | |
244 | // * @return | |
245 | // * @throws IOException | |
246 | // * @throws ParseException | |
247 | // */ | |
248 | // protected Object getSequenceJSON(List<String> ids, int mode) | |
249 | // throws IOException, ParseException | |
250 | // { | |
251 | // URL url = getUrl(ids); | |
252 | // return getJSON(url, ids, -1, mode); | |
253 | // } | |
254 | // | |
255 | // /** | |
256 | // * Gets a reader to the HTTP response, using the default read timeout of 5 | |
257 | // * minutes | |
258 | // * | |
259 | // * @param url | |
260 | // * @param ids | |
261 | // * @return | |
262 | // * @throws IOException | |
263 | // */ | |
264 | // protected BufferedReader getHttpResponse(URL url, List<String> ids) | |
265 | // throws IOException | |
266 | // { | |
267 | // return getHttpResponse(url, ids, DEFAULT_READ_TIMEOUT); | |
268 | // } | |
269 | ||
270 | /** | |
271 | * Sends the HTTP request and gets the response as a reader. Returns null if | |
272 | * the HTTP response code was not 200. | |
273 | * | |
274 | * @param url | |
275 | * @param ids | |
276 | * written as Json POST body if more than one | |
277 | * @param readTimeout | |
278 | * in milliseconds | |
279 | * @return | |
280 | * @throws IOException | |
281 | * @throws ParseException | |
282 | */ | |
283 | 0 | private Object getJSON(URL url, List<String> ids, int readTimeout) |
284 | throws IOException, ParseException | |
285 | { | |
286 | ||
287 | 0 | if (readTimeout < 0) |
288 | { | |
289 | 0 | readTimeout = DEFAULT_READ_TIMEOUT; |
290 | } | |
291 | 0 | int retriesLeft = MAX_RETRIES; |
292 | 0 | HttpURLConnection connection = null; |
293 | 0 | int responseCode = 0; |
294 | ||
295 | 0 | Platform.setAjaxJSON(url); |
296 | ||
297 | 0 | while (retriesLeft > 0) |
298 | { | |
299 | 0 | connection = tryConnection(url, ids, readTimeout); |
300 | 0 | responseCode = connection.getResponseCode(); |
301 | 0 | if (responseCode == HTTP_OVERLOAD) // 429 |
302 | { | |
303 | 0 | retriesLeft--; |
304 | 0 | checkRetryAfter(connection); |
305 | } | |
306 | else | |
307 | { | |
308 | 0 | retriesLeft = 0; |
309 | } | |
310 | } | |
311 | 0 | if (responseCode != HTTP_OK) // 200 |
312 | { | |
313 | /* | |
314 | * note: a GET request for an invalid id returns an error code e.g. 415 | |
315 | * but POST request returns 200 and an empty Fasta response | |
316 | */ | |
317 | 0 | jalview.bin.Console.errPrintln("Response code " + responseCode);// + " for |
318 | // " + | |
319 | // url); | |
320 | 0 | return null; |
321 | } | |
322 | ||
323 | 0 | InputStream response = connection.getInputStream(); |
324 | ||
325 | // Platform.timeCheck(null, Platform.TIME_MARK); | |
326 | 0 | Object ret = Platform.parseJSON(response); |
327 | // Platform.timeCheck("EnsemblRestClient.getJSON " + url, | |
328 | // Platform.TIME_MARK); | |
329 | ||
330 | 0 | return ret; |
331 | } | |
332 | ||
333 | /** | |
334 | * @param url | |
335 | * @param ids | |
336 | * @param readTimeout | |
337 | * @return | |
338 | * @throws IOException | |
339 | * @throws ProtocolException | |
340 | */ | |
341 | 0 | protected HttpURLConnection tryConnection(URL url, List<String> ids, |
342 | int readTimeout) throws IOException, ProtocolException | |
343 | { | |
344 | // jalview.bin.Console.outPrintln(System.currentTimeMillis() + " " + url); | |
345 | ||
346 | 0 | HttpURLConnection connection = (HttpURLConnection) HttpUtils |
347 | .openConnection(url); | |
348 | ||
349 | /* | |
350 | * POST method allows multiple queries in one request; it is supported for | |
351 | * sequence queries, but not for overlap | |
352 | */ | |
353 | 0 | boolean multipleIds = ids != null && ids.size() > 1; |
354 | 0 | connection.setRequestMethod( |
355 | 0 | multipleIds ? HttpMethod.POST : HttpMethod.GET); |
356 | 0 | connection.setRequestProperty("Content-Type", getRequestMimeType()); |
357 | 0 | connection.setRequestProperty("Accept", getResponseMimeType()); |
358 | ||
359 | 0 | connection.setDoInput(true); |
360 | 0 | connection.setDoOutput(multipleIds); |
361 | ||
362 | 0 | connection.setUseCaches(false); |
363 | 0 | connection.setConnectTimeout(CONNECT_TIMEOUT_MS); |
364 | 0 | connection.setReadTimeout(readTimeout); |
365 | ||
366 | 0 | if (multipleIds) |
367 | { | |
368 | 0 | writePostBody(connection, ids); |
369 | } | |
370 | 0 | return connection; |
371 | } | |
372 | ||
373 | /** | |
374 | * Inspects response headers for a 'retry-after' directive, and waits for the | |
375 | * directed period (if less than 10 seconds) | |
376 | * | |
377 | * @see https://github.com/Ensembl/ensembl-rest/wiki/Rate-Limits | |
378 | * @param connection | |
379 | */ | |
380 | 0 | void checkRetryAfter(HttpURLConnection connection) |
381 | { | |
382 | 0 | String retryDelay = connection.getHeaderField("Retry-After"); |
383 | ||
384 | // to test: | |
385 | // retryDelay = "5"; | |
386 | ||
387 | 0 | if (retryDelay != null) |
388 | { | |
389 | 0 | try |
390 | { | |
391 | 0 | int retrySecs = Integer.valueOf(retryDelay); |
392 | 0 | if (retrySecs > 0 && retrySecs < 10) |
393 | { | |
394 | 0 | jalview.bin.Console.errPrintln( |
395 | "Ensembl REST service rate limit exceeded, waiting " | |
396 | + retryDelay + " seconds before retrying"); | |
397 | 0 | Thread.sleep(1000 * retrySecs); |
398 | } | |
399 | } catch (NumberFormatException | InterruptedException e) | |
400 | { | |
401 | 0 | jalview.bin.Console.errPrintln( |
402 | "Error handling Retry-After: " + e.getMessage()); | |
403 | } | |
404 | } | |
405 | } | |
406 | ||
407 | /** | |
408 | * Rechecks if Ensembl is responding, unless the last check was successful and | |
409 | * the retest interval has not yet elapsed. Returns true if Ensembl is up, | |
410 | * else false. Also retrieves and saves the current version of Ensembl data | |
411 | * and REST services at intervals. | |
412 | * | |
413 | * @return | |
414 | */ | |
415 | 0 | protected boolean isEnsemblAvailable() |
416 | { | |
417 | 0 | EnsemblData info = domainData.get(getDomain()); |
418 | ||
419 | 0 | long now = System.currentTimeMillis(); |
420 | ||
421 | /* | |
422 | * recheck if Ensembl is up if it was down, or the recheck period has elapsed | |
423 | */ | |
424 | 0 | boolean retestAvailability = (now |
425 | - info.lastAvailableCheckTime) > AVAILABILITY_RETEST_INTERVAL; | |
426 | 0 | if (!info.restAvailable || retestAvailability) |
427 | { | |
428 | 0 | info.restAvailable = checkEnsembl(); |
429 | 0 | info.lastAvailableCheckTime = now; |
430 | } | |
431 | ||
432 | /* | |
433 | * refetch Ensembl versions if the recheck period has elapsed | |
434 | */ | |
435 | 0 | boolean refetchVersion = (now |
436 | - info.lastVersionCheckTime) > VERSION_RETEST_INTERVAL; | |
437 | 0 | if (refetchVersion) |
438 | { | |
439 | 0 | checkEnsemblRestVersion(); |
440 | 0 | checkEnsemblDataVersion(); |
441 | 0 | info.lastVersionCheckTime = now; |
442 | } | |
443 | ||
444 | 0 | return info.restAvailable; |
445 | } | |
446 | ||
447 | /** | |
448 | * Constructs, writes and flushes the POST body of the request, containing the | |
449 | * query ids in JSON format | |
450 | * | |
451 | * @param connection | |
452 | * @param ids | |
453 | * @throws IOException | |
454 | */ | |
455 | 0 | protected void writePostBody(HttpURLConnection connection, |
456 | List<String> ids) throws IOException | |
457 | { | |
458 | 0 | boolean first; |
459 | 0 | StringBuilder postBody = new StringBuilder(64); |
460 | 0 | postBody.append("{\"ids\":["); |
461 | 0 | first = true; |
462 | 0 | for (int i = 0, n = ids.size(); i < n; i++) |
463 | { | |
464 | 0 | String id = ids.get(i); |
465 | 0 | if (!first) |
466 | { | |
467 | 0 | postBody.append(","); |
468 | } | |
469 | 0 | first = false; |
470 | 0 | postBody.append("\""); |
471 | 0 | postBody.append(id.trim()); |
472 | 0 | postBody.append("\""); |
473 | } | |
474 | 0 | postBody.append("]}"); |
475 | 0 | byte[] thepostbody = postBody.toString().getBytes(); |
476 | 0 | connection.setRequestProperty("Content-Length", |
477 | Integer.toString(thepostbody.length)); | |
478 | 0 | DataOutputStream wr = new DataOutputStream( |
479 | connection.getOutputStream()); | |
480 | 0 | wr.write(thepostbody); |
481 | 0 | wr.flush(); |
482 | 0 | wr.close(); |
483 | } | |
484 | ||
485 | /** | |
486 | * Primary access point to parsed JSON data, including the call to retrieve | |
487 | * and parsing. | |
488 | * | |
489 | * @param url | |
490 | * request url; if null, getUrl(ids) will be used | |
491 | * @param ids | |
492 | * optional; may be null | |
493 | * @param msDelay | |
494 | * -1 for default delay | |
495 | * @param mode | |
496 | * map, array, or array iterator | |
497 | * @param mapKey | |
498 | * an optional key for an outer map | |
499 | * @return a Map, List, Iterator, or null | |
500 | * @throws IOException | |
501 | * @throws ParseException | |
502 | * | |
503 | * @author Bob Hanson 2019 | |
504 | */ | |
505 | 0 | @SuppressWarnings("unchecked") |
506 | protected Object getJSON(URL url, List<String> ids, int msDelay, int mode, | |
507 | String mapKey) throws IOException, ParseException | |
508 | { | |
509 | 0 | if (url == null) |
510 | { | |
511 | 0 | url = getUrl(ids); |
512 | } | |
513 | ||
514 | 0 | Object json = (url == null ? null : getJSON(url, ids, msDelay)); |
515 | ||
516 | 0 | if (json != null && mapKey != null) |
517 | { | |
518 | 0 | json = ((Map<String, Object>) json).get(mapKey); |
519 | } | |
520 | 0 | if (json == null) |
521 | { | |
522 | 0 | return null; |
523 | } | |
524 | 0 | switch (mode) |
525 | { | |
526 | 0 | case MODE_ARRAY: |
527 | 0 | case MODE_MAP: |
528 | 0 | break; |
529 | 0 | case MODE_ITERATOR: |
530 | 0 | json = ((List<Object>) json).iterator(); |
531 | 0 | break; |
532 | } | |
533 | 0 | return json; |
534 | } | |
535 | ||
536 | /** | |
537 | * Fetches and checks Ensembl's REST version number | |
538 | * | |
539 | * @return | |
540 | */ | |
541 | 0 | @SuppressWarnings("unchecked") |
542 | private void checkEnsemblRestVersion() | |
543 | { | |
544 | 0 | EnsemblData info = domainData.get(getDomain()); |
545 | ||
546 | 0 | try |
547 | { | |
548 | 0 | Map<String, Object> val = (Map<String, Object>) getJSON( |
549 | new URL(getDomain() + "/info/rest" + CONTENT_TYPE_JSON), null, | |
550 | -1, MODE_MAP, null); | |
551 | 0 | if (val == null) |
552 | { | |
553 | 0 | return; |
554 | } | |
555 | 0 | String version = val.get("release").toString(); |
556 | 0 | String majorVersion = version.substring(0, version.indexOf(".")); |
557 | 0 | String expected = info.expectedRestVersion; |
558 | 0 | String expectedMajorVersion = expected.substring(0, |
559 | expected.indexOf(".")); | |
560 | 0 | info.restMajorVersionMismatch = false; |
561 | 0 | try |
562 | { | |
563 | /* | |
564 | * if actual REST major version is ahead of what we expect, | |
565 | * record this in case we want to warn the user | |
566 | */ | |
567 | 0 | if (Float.valueOf(majorVersion) > Float |
568 | .valueOf(expectedMajorVersion)) | |
569 | { | |
570 | 0 | info.restMajorVersionMismatch = true; |
571 | } | |
572 | } catch (NumberFormatException e) | |
573 | { | |
574 | 0 | jalview.bin.Console |
575 | .errPrintln("Error in REST version: " + e.toString()); | |
576 | } | |
577 | ||
578 | /* | |
579 | * check if REST version is later than what Jalview has tested against, | |
580 | * if so warn; we don't worry if it is earlier (this indicates Jalview has | |
581 | * been tested in advance against the next pending REST version) | |
582 | */ | |
583 | 0 | boolean laterVersion = StringUtils.compareVersions(version, |
584 | expected) == 1; | |
585 | 0 | if (laterVersion) |
586 | { | |
587 | 0 | jalview.bin.Console.errPrintln(String.format( |
588 | "EnsemblRestClient expected %s REST version %s but found %s, see %s", | |
589 | getDbSource(), expected, version, REST_CHANGE_LOG)); | |
590 | } | |
591 | 0 | info.restVersion = version; |
592 | } catch (Throwable t) | |
593 | { | |
594 | 0 | jalview.bin.Console.errPrintln( |
595 | "Error checking Ensembl REST version: " + t.getMessage()); | |
596 | } | |
597 | } | |
598 | ||
599 | 0 | public boolean isRestMajorVersionMismatch() |
600 | { | |
601 | 0 | return domainData.get(getDomain()).restMajorVersionMismatch; |
602 | } | |
603 | ||
604 | /** | |
605 | * Fetches and checks Ensembl's data version number | |
606 | * | |
607 | * @return | |
608 | */ | |
609 | 0 | @SuppressWarnings("unchecked") |
610 | private void checkEnsemblDataVersion() | |
611 | { | |
612 | 0 | Map<String, Object> val; |
613 | 0 | try |
614 | { | |
615 | 0 | val = (Map<String, Object>) getJSON( |
616 | new URL(getDomain() + "/info/data" + CONTENT_TYPE_JSON), null, | |
617 | -1, MODE_MAP, null); | |
618 | 0 | if (val == null) |
619 | { | |
620 | 0 | return; |
621 | } | |
622 | 0 | List<Object> versions = (List<Object>) val.get("releases"); |
623 | 0 | domainData.get(getDomain()).dataVersion = versions.get(0).toString(); |
624 | } catch (Throwable e) | |
625 | {// could be IOException | ParseException e) { | |
626 | 0 | jalview.bin.Console.errPrintln( |
627 | "Error checking Ensembl data version: " + e.getMessage()); | |
628 | } | |
629 | } | |
630 | ||
631 | 0 | public String getEnsemblDataVersion() |
632 | { | |
633 | 0 | return domainData.get(getDomain()).dataVersion; |
634 | } | |
635 | ||
636 | 0 | @Override |
637 | public String getDbVersion() | |
638 | { | |
639 | 0 | return getEnsemblDataVersion(); |
640 | } | |
641 | ||
642 | } |