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