JAL-4059 Added some more Platform.addJ2SDirectDatabaseCall web service URLs in static...
[jalview.git] / src / jalview / fts / service / threedbeacons / TDBeaconsFTSRestClient.java
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.fts.service.threedbeacons;
22
23 import java.net.URI;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29
30 import javax.ws.rs.core.MediaType;
31
32 import org.json.simple.parser.ParseException;
33
34 import com.sun.jersey.api.client.Client;
35 import com.sun.jersey.api.client.ClientResponse;
36 import com.sun.jersey.api.client.WebResource;
37 import com.sun.jersey.api.client.config.DefaultClientConfig;
38
39 import jalview.bin.Console;
40 import jalview.datamodel.SequenceI;
41 import jalview.fts.api.FTSData;
42 import jalview.fts.api.FTSDataColumnI;
43 import jalview.fts.api.FTSRestClientI;
44 import jalview.fts.api.StructureFTSRestClientI;
45 import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
46 import jalview.fts.core.FTSRestClient;
47 import jalview.fts.core.FTSRestRequest;
48 import jalview.fts.core.FTSRestResponse;
49 import jalview.util.JSONUtils;
50 import jalview.util.MessageManager;
51 import jalview.util.Platform;
52
53 public class TDBeaconsFTSRestClient extends FTSRestClient
54         implements StructureFTSRestClientI
55 {
56   /**
57    * production server URI
58    */
59   private static String TDB_PROD_API = "https://www.ebi.ac.uk/pdbe/pdbe-kb/3dbeacons/api/uniprot/summary/";
60
61   /**
62    * dev server URI
63    */
64   private static String TDB_DEV_API = "https://wwwdev.ebi.ac.uk/pdbe/pdbe-kb/3dbeacons/api/uniprot/summary/";
65
66   private static String DEFAULT_THREEDBEACONS_DOMAIN = TDB_PROD_API;
67
68   public static FTSRestClientI instance = null;
69
70   static
71   {
72     Platform.addJ2SDirectDatabaseCall(DEFAULT_THREEDBEACONS_DOMAIN);
73   }
74
75   protected TDBeaconsFTSRestClient()
76   {
77   }
78
79   @SuppressWarnings("unchecked")
80   @Override
81   public FTSRestResponse executeRequest(FTSRestRequest tdbRestRequest)
82           throws Exception
83   {
84     try
85     {
86       String query = tdbRestRequest.getSearchTerm();
87       Client client;
88       Class<ClientResponse> clientResponseClass;
89       if (Platform.isJS())
90       {
91         // JavaScript only
92         client = (Client) (Object) new jalview.javascript.web.Client();
93         clientResponseClass = (Class<ClientResponse>) (Object) jalview.javascript.web.ClientResponse.class;
94       }
95       else
96       /**
97        * Java only
98        * 
99        * @j2sIgnore
100        */
101       {
102         client = Client.create(new DefaultClientConfig());
103         clientResponseClass = ClientResponse.class;
104       }
105
106       WebResource webResource;
107       webResource = client.resource(DEFAULT_THREEDBEACONS_DOMAIN + query);
108
109       URI uri = webResource.getURI();
110       Console.info("3DBeacons URL: " + uri.toString());
111
112       // Execute the REST request
113       ClientResponse clientResponse;
114       if (isMocked())
115       {
116         clientResponse = null;
117       }
118       else
119       {
120         clientResponse = webResource.accept(MediaType.APPLICATION_JSON)
121                 .get(clientResponseClass);
122       }
123
124       // Get the JSON string from the response object or directly from the
125       // client (JavaScript)
126       Map<String, Object> jsonObj = null;
127       String responseString = null;
128
129       // Check the response status and report exception if one occurs
130       int responseStatus = isMocked()
131               ? (mockQueries.containsKey(query) ? 200 : 404)
132               : clientResponse.getStatus();
133       switch (responseStatus)
134       {
135       // if success
136       case 200:
137         if (Platform.isJS())
138         {
139           jsonObj = clientResponse.getEntity(Map.class);
140         }
141         else
142         {
143           responseString = isMocked() ? mockQueries.get(query)
144                   : clientResponse.getEntity(String.class);
145         }
146         break;
147       case 400:
148         throw new Exception(parseJsonExceptionString(responseString));
149       case 404:
150         return emptyTDBeaconsJsonResponse();
151       default:
152         throw new Exception(
153                 getMessageByHTTPStatusCode(responseStatus, "3DBeacons"));
154       }
155       // Process the response and return the result to the caller.
156       return parseTDBeaconsJsonResponse(responseString, jsonObj,
157               tdbRestRequest);
158     } catch (Exception e)
159     {
160       String exceptionMsg = e.getMessage();
161       if (exceptionMsg != null)
162       {
163         if (exceptionMsg.contains("SocketException"))
164         {
165           // No internet connection
166           throw new Exception(MessageManager.getString(
167                   "exception.unable_to_detect_internet_connection"));
168         }
169         else if (exceptionMsg.contains("UnknownHostException"))
170         {
171           // The server is unreachable
172           throw new Exception(MessageManager.formatMessage(
173                   "exception.fts_server_unreachable", "3DB Hub"));
174         }
175       }
176       throw e;
177
178     }
179
180   }
181
182   /**
183    * returns response for when the 3D-Beacons service doesn't have a record for
184    * the given query - in 2.11.2 this triggers a failover to the PDBe FTS
185    * 
186    * @return null
187    */
188   private FTSRestResponse emptyTDBeaconsJsonResponse()
189   {
190     return null;
191   }
192
193   public String setSearchTerm(String term)
194   {
195     return term;
196   }
197
198   public static FTSRestResponse parseTDBeaconsJsonResponse(
199           String tdbJsonResponseString, FTSRestRequest tdbRestRequest)
200   {
201     return parseTDBeaconsJsonResponse(tdbJsonResponseString,
202             (Map<String, Object>) null, tdbRestRequest);
203   }
204
205   @SuppressWarnings("unchecked")
206   public static FTSRestResponse parseTDBeaconsJsonResponse(
207           String tdbJsonResponseString, Map<String, Object> jsonObj,
208           FTSRestRequest tdbRestRequest)
209   {
210     FTSRestResponse searchResult = new FTSRestResponse();
211     List<FTSData> result = null;
212
213     try
214     {
215       if (jsonObj == null)
216       {
217         jsonObj = (Map<String, Object>) JSONUtils
218                 .parse(tdbJsonResponseString);
219       }
220
221       Object uniprot_entry = jsonObj.get("uniprot_entry");
222       // TODO: decide if anything from uniprot_entry needs to be reported via
223       // the FTSRestResponse object
224       // Arnaud added seqLength = (Long) ((Map<String, Object>)
225       // jsonObj.get("uniprot_entry")).get("sequence_length");
226
227       List<Object> structures = (List<Object>) jsonObj.get("structures");
228       result = new ArrayList<>();
229
230       int numFound = 0;
231       for (Iterator<Object> strucIter = structures.iterator(); strucIter
232               .hasNext();)
233       {
234         Map<String, Object> structure = (Map<String, Object>) strucIter
235                 .next();
236         result.add(getFTSData(structure, tdbRestRequest));
237         numFound++;
238       }
239
240       searchResult.setNumberOfItemsFound(numFound);
241       searchResult.setSearchSummary(result);
242
243     } catch (ParseException e)
244     {
245       e.printStackTrace();
246     }
247     return searchResult;
248   }
249
250   private static FTSData getFTSData(
251           Map<String, Object> tdbJsonStructureSummary,
252           FTSRestRequest tdbRequest)
253   {
254     String primaryKey = null;
255     Object[] summaryRowData;
256
257     SequenceI associatedSequence;
258
259     Collection<FTSDataColumnI> displayFields = tdbRequest.getWantedFields();
260     SequenceI associatedSeq = tdbRequest.getAssociatedSequence();
261     int colCounter = 0;
262     summaryRowData = new Object[(associatedSeq != null)
263             ? displayFields.size() + 1
264             : displayFields.size()];
265     if (associatedSeq != null)
266     {
267       associatedSequence = associatedSeq;
268       summaryRowData[0] = associatedSequence;
269       colCounter = 1;
270     }
271     Map<String, Object> tdbJsonStructure = (Map<String, Object>) tdbJsonStructureSummary
272             .get("summary");
273     for (FTSDataColumnI field : displayFields)
274     {
275       String fieldData = (tdbJsonStructure.get(field.getCode()) == null)
276               ? " "
277               : tdbJsonStructure.get(field.getCode()).toString();
278       // Console.outPrintln("Field : " + field + " Data : " + fieldData);
279       if (field.isPrimaryKeyColumn())
280       {
281         primaryKey = fieldData;
282         summaryRowData[colCounter++] = primaryKey;
283       }
284       else if (fieldData == null || fieldData.trim().isEmpty())
285       {
286         summaryRowData[colCounter++] = null;
287       }
288       else
289       {
290         try
291         {
292           summaryRowData[colCounter++] = (field.getDataType()
293                   .getDataTypeClass() == Integer.class)
294                           ? Integer.valueOf(fieldData)
295                           : (field.getDataType()
296                                   .getDataTypeClass() == Double.class)
297                                           ? Double.valueOf(fieldData)
298                                           : fieldData;
299         } catch (Exception e)
300         {
301           // e.printStackTrace();
302           Console.warn("offending value:" + fieldData + fieldData);
303         }
304       }
305     }
306     final String primaryKey1 = primaryKey;
307     final Object[] summaryRowData1 = summaryRowData;
308
309     return new TDB_FTSData(primaryKey, tdbJsonStructure, summaryRowData1);
310   }
311
312   // private static FTSData getFTSData(Map<String, Object> doc,
313   // FTSRestRequest tdbRestRequest)
314   // {
315   // String primaryKey = null;
316   //
317   // Object[] summaryRowData;
318   //
319   // Collection<FTSDataColumnI> displayFields =
320   // tdbRestRequest.getWantedFields();
321   // int colCounter = 0;
322   // summaryRowData = new Object[displayFields.size() + 1];
323   //
324   // return null;
325   // }
326
327   private String parseJsonExceptionString(String jsonErrorString)
328   {
329     // TODO Auto-generated method stub
330     return null;
331   }
332
333   @Override
334   public String getColumnDataConfigFileName()
335   {
336     return "/fts/tdbeacons_data_columns.txt";
337   }
338
339   public static FTSRestClientI getInstance()
340   {
341     if (instance == null)
342     {
343       instance = new TDBeaconsFTSRestClient();
344     }
345     return instance;
346   }
347
348   private Collection<FTSDataColumnI> allDefaultDisplayedStructureDataColumns;
349
350   @Override
351   public Collection<FTSDataColumnI> getAllDefaultDisplayedStructureDataColumns()
352   {
353     if (allDefaultDisplayedStructureDataColumns == null
354             || allDefaultDisplayedStructureDataColumns.isEmpty())
355     {
356       allDefaultDisplayedStructureDataColumns = new ArrayList<>();
357       allDefaultDisplayedStructureDataColumns
358               .addAll(super.getAllDefaultDisplayedFTSDataColumns());
359     }
360     return allDefaultDisplayedStructureDataColumns;
361   }
362
363   @Override
364   public String[] getPreferencesColumnsFor(PreferenceSource source)
365   {
366     String[] columnNames = null;
367     switch (source)
368     {
369     case SEARCH_SUMMARY:
370       columnNames = new String[] { "", "Display", "Group" };
371       break;
372     case STRUCTURE_CHOOSER:
373       columnNames = new String[] { "", "Display", "Group" };
374       break;
375     case PREFERENCES:
376       columnNames = new String[] { "3DB Beacons Field",
377           "Show in search summary", "Show in structure summary" };
378       break;
379     default:
380       break;
381     }
382     return columnNames;
383   }
384 }