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