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