Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package jalview.fts.service.pdb

File PDBFTSRestClient.java

 

Coverage histogram

../../../../img/srcFileCovDistChart3.png
47% of files have more coverage

Code metrics

52
125
15
1
485
341
51
0.41
8.33
15
3.4

Classes

Class Line # Actions
PDBFTSRestClient 56 125 51
0.3020833430.2%
 

Contributing tests

This file is covered by 4 tests. .

Source view

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.pdb;
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.core.FTSRestClient;
45    import jalview.fts.core.FTSRestRequest;
46    import jalview.fts.core.FTSRestResponse;
47    import jalview.util.JSONUtils;
48    import jalview.util.MessageManager;
49    import jalview.util.Platform;
50   
51    /**
52    * A rest client for querying the Search endpoint of the PDB API
53    *
54    * @author tcnofoegbu
55    */
 
56    public class PDBFTSRestClient extends FTSRestClient
57    {
58   
59    private static FTSRestClientI instance = null;
60   
61    public static final String PDB_SEARCH_ENDPOINT = "https://www.ebi.ac.uk/pdbe/search/pdb/select?";
62   
 
63  1 toggle protected PDBFTSRestClient()
64    {
65    }
66   
67    /**
68    * Takes a PDBRestRequest object and returns a response upon execution
69    *
70    * @param pdbRestRequest
71    * the PDBRestRequest instance to be processed
72    * @return the pdbResponse object for the given request
73    * @throws Exception
74    */
 
75  1 toggle @SuppressWarnings({ "unused", "unchecked" })
76    @Override
77    public FTSRestResponse executeRequest(FTSRestRequest pdbRestRequest)
78    throws Exception
79    {
80  1 try
81    {
82  1 String wantedFields = getDataColumnsFieldsAsCommaDelimitedString(
83    pdbRestRequest.getWantedFields());
84  1 int responseSize = (pdbRestRequest.getResponseSize() == 0)
85    ? getDefaultResponsePageSize()
86    : pdbRestRequest.getResponseSize();
87  1 int offSet = pdbRestRequest.getOffSet();
88  1 String sortParam = null;
89  1 if (pdbRestRequest.getFieldToSortBy() == null
90    || pdbRestRequest.getFieldToSortBy().trim().isEmpty())
91    {
92  0 sortParam = "";
93    }
94    else
95    {
96  1 if (pdbRestRequest.getFieldToSortBy()
97    .equalsIgnoreCase("Resolution"))
98    {
99  0 sortParam = pdbRestRequest.getFieldToSortBy()
100  0 + (pdbRestRequest.isAscending() ? " asc" : " desc");
101    }
102    else
103    {
104  1 sortParam = pdbRestRequest.getFieldToSortBy()
105  1 + (pdbRestRequest.isAscending() ? " desc" : " asc");
106    }
107    }
108   
109  1 String facetPivot = (pdbRestRequest.getFacetPivot() == null
110    || pdbRestRequest.getFacetPivot().isEmpty()) ? ""
111    : pdbRestRequest.getFacetPivot();
112  1 String facetPivotMinCount = String
113    .valueOf(pdbRestRequest.getFacetPivotMinCount());
114   
115  1 String query = pdbRestRequest.getFieldToSearchBy()
116    + pdbRestRequest.getSearchTerm()
117  1 + (pdbRestRequest.isAllowEmptySeq() ? ""
118    : " AND molecule_sequence:['' TO *]")
119  1 + (pdbRestRequest.isAllowUnpublishedEntries() ? ""
120    : " AND status:REL");
121   
122    // Build request parameters for the REST Request
123   
124    // BH 2018 the trick here is to coerce the classes in Javascript to be
125    // different from the ones in Java yet still allow this to be correct for Java
126  1 Client client;
127  1 Class<ClientResponse> clientResponseClass;
128  1 if (Platform.isJS())
129    {
130    // JavaScript only -- coerce types to Java types for Java
131  0 client = (Client) (Object) new jalview.javascript.web.Client();
132  0 clientResponseClass = (Class<ClientResponse>) (Object) jalview.javascript.web.ClientResponse.class;
133    }
134    else
135    /**
136    * Java only
137    *
138    * @j2sIgnore
139    */
140    {
141  1 client = Client.create(new DefaultClientConfig());
142  1 clientResponseClass = ClientResponse.class;
143    }
144   
145  1 WebResource webResource;
146  1 if (pdbRestRequest.isFacet())
147    {
148  0 webResource = client.resource(PDB_SEARCH_ENDPOINT)
149    .queryParam("wt", "json").queryParam("fl", wantedFields)
150    .queryParam("rows", String.valueOf(responseSize))
151    .queryParam("q", query)
152    .queryParam("start", String.valueOf(offSet))
153    .queryParam("sort", sortParam).queryParam("facet", "true")
154    .queryParam("facet.pivot", facetPivot)
155    .queryParam("facet.pivot.mincount", facetPivotMinCount);
156    }
157    else
158    {
159  1 webResource = client.resource(PDB_SEARCH_ENDPOINT)
160    .queryParam("wt", "json").queryParam("fl", wantedFields)
161    .queryParam("rows", String.valueOf(responseSize))
162    .queryParam("start", String.valueOf(offSet))
163    .queryParam("q", query).queryParam("sort", sortParam);
164    }
165   
166  1 URI uri = webResource.getURI();
167   
168    // System.out.println(uri);
169   
170    // Execute the REST request
171  1 ClientResponse clientResponse = webResource
172    .accept(MediaType.APPLICATION_JSON).get(clientResponseClass );
173   
174    // Get the JSON string from the response object or directly from the
175    // client (JavaScript)
176  1 Map<String, Object> jsonObj = null;
177  1 String responseString = null;
178   
179    // System.out.println("query >>>>>>> " + pdbRestRequest.toString());
180   
181    // Check the response status and report exception if one occurs
182  1 int responseStatus = clientResponse.getStatus();
183  1 switch (responseStatus)
184    {
185  0 case 200:
186  0 if (Platform.isJS())
187    {
188  0 jsonObj = clientResponse.getEntity(Map.class);
189    }
190    else
191    {
192  0 responseString = clientResponse.getEntity(String.class);
193    }
194  0 break;
195  1 case 400:
196  1 throw new Exception(parseJsonExceptionString(responseString));
197  0 default:
198  0 throw new Exception(
199    getMessageByHTTPStatusCode(responseStatus, "PDB"));
200    }
201   
202    // Process the response and return the result to the caller.
203  0 return parsePDBJsonResponse(responseString, jsonObj, pdbRestRequest);
204    } catch (Exception e)
205    {
206  1 String exceptionMsg = e.getMessage();
207  0 if (exceptionMsg.contains("SocketException"))
208    {
209    // No internet connection
210  0 throw new Exception(MessageManager.getString(
211    "exception.unable_to_detect_internet_connection"));
212    }
213  0 else if (exceptionMsg.contains("UnknownHostException"))
214    {
215    // The server 'www.ebi.ac.uk' is unreachable
216  0 throw new Exception(MessageManager.formatMessage(
217    "exception.fts_server_unreachable", "PDB Solr"));
218    }
219    else
220    {
221  0 throw e;
222    }
223    }
224    }
225   
226    /**
227    * Process error response from PDB server if/when one occurs.
228    *
229    * @param jsonResponse
230    * the JSON string containing error message from the server
231    * @return the processed error message from the JSON string
232    */
 
233  1 toggle @SuppressWarnings("unchecked")
234    public static String parseJsonExceptionString(String jsonErrorResponse)
235    {
236  1 StringBuilder errorMessage = new StringBuilder(
237    "\n============= PDB Rest Client RunTime error =============\n");
238   
239   
240    // {
241    // "responseHeader":{
242    // "status":0,
243    // "QTime":0,
244    // "params":{
245    // "q":"(text:q93xj9_soltu) AND molecule_sequence:['' TO *] AND status:REL",
246    // "fl":"pdb_id,title,experimental_method,resolution",
247    // "start":"0",
248    // "sort":"overall_quality desc",
249    // "rows":"500",
250    // "wt":"json"}},
251    // "response":{"numFound":1,"start":0,"docs":[
252    // {
253    // "experimental_method":["X-ray diffraction"],
254    // "pdb_id":"4zhp",
255    // "resolution":2.46,
256    // "title":"The crystal structure of Potato ferredoxin I with 2Fe-2S cluster"}]
257    // }}
258    //
259  1 try
260    {
261  1 Map<String, Object> jsonObj = (Map<String, Object>) JSONUtils.parse(jsonErrorResponse);
262  0 Map<String, Object> errorResponse = (Map<String, Object>) jsonObj.get("error");
263   
264  0 Map<String, Object> responseHeader = (Map<String, Object>) jsonObj
265    .get("responseHeader");
266  0 Map<String, Object> paramsObj = (Map<String, Object>) responseHeader.get("params");
267  0 String status = responseHeader.get("status").toString();
268  0 String message = errorResponse.get("msg").toString();
269  0 String query = paramsObj.get("q").toString();
270  0 String fl = paramsObj.get("fl").toString();
271   
272  0 errorMessage.append("Status: ").append(status).append("\n");
273  0 errorMessage.append("Message: ").append(message).append("\n");
274  0 errorMessage.append("query: ").append(query).append("\n");
275  0 errorMessage.append("fl: ").append(fl).append("\n");
276   
277    } catch (ParseException e)
278    {
279  0 e.printStackTrace();
280    }
281  0 return errorMessage.toString();
282    }
283   
284    /**
285    * Parses the JSON response string from PDB REST API. The response is dynamic
286    * hence, only fields specifically requested for in the 'wantedFields'
287    * parameter is fetched/processed
288    *
289    * @param pdbJsonResponseString
290    * the JSON string to be parsed
291    * @param pdbRestRequest
292    * the request object which contains parameters used to process the
293    * JSON string
294    * @return
295    */
 
296  0 toggle public static FTSRestResponse parsePDBJsonResponse(
297    String pdbJsonResponseString, FTSRestRequest pdbRestRequest)
298    {
299  0 return parsePDBJsonResponse(pdbJsonResponseString,
300    (Map<String, Object>) null, pdbRestRequest);
301    }
302   
 
303  0 toggle @SuppressWarnings("unchecked")
304    public static FTSRestResponse parsePDBJsonResponse(
305    String pdbJsonResponseString, Map<String, Object> jsonObj,
306    FTSRestRequest pdbRestRequest)
307    {
308  0 FTSRestResponse searchResult = new FTSRestResponse();
309  0 List<FTSData> result = null;
310  0 try
311    {
312  0 if (jsonObj == null)
313    {
314  0 jsonObj = (Map<String, Object>) JSONUtils.parse(pdbJsonResponseString);
315    }
316  0 Map<String, Object> pdbResponse = (Map<String, Object>) jsonObj.get("response");
317  0 String queryTime = ((Map<String, Object>) jsonObj.get("responseHeader"))
318    .get("QTime").toString();
319  0 int numFound = Integer
320    .valueOf(pdbResponse.get("numFound").toString());
321  0 if (numFound > 0)
322    {
323  0 result = new ArrayList<>();
324  0 List<Object> docs = (List<Object>) pdbResponse.get("docs");
325  0 for (Iterator<Object> docIter = docs.iterator(); docIter
326    .hasNext();)
327    {
328  0 Map<String, Object> doc = (Map<String, Object>) docIter.next();
329  0 result.add(getFTSData(doc, pdbRestRequest));
330    }
331  0 searchResult.setNumberOfItemsFound(numFound);
332  0 searchResult.setResponseTime(queryTime);
333  0 searchResult.setSearchSummary(result);
334    }
335    } catch (ParseException e)
336    {
337  0 e.printStackTrace();
338    }
339  0 return searchResult;
340    }
341   
 
342  0 toggle public static FTSData getFTSData(Map<String, Object> pdbJsonDoc,
343    FTSRestRequest request)
344    {
345   
346  0 String primaryKey = null;
347   
348  0 Object[] summaryRowData;
349   
350  0 SequenceI associatedSequence;
351   
352  0 Collection<FTSDataColumnI> diplayFields = request.getWantedFields();
353  0 SequenceI associatedSeq = request.getAssociatedSequence();
354  0 int colCounter = 0;
355  0 summaryRowData = new Object[(associatedSeq != null)
356    ? diplayFields.size() + 1
357    : diplayFields.size()];
358  0 if (associatedSeq != null)
359    {
360  0 associatedSequence = associatedSeq;
361  0 summaryRowData[0] = associatedSequence;
362  0 colCounter = 1;
363    }
364   
365  0 for (FTSDataColumnI field : diplayFields)
366    {
367  0 String fieldData = (pdbJsonDoc.get(field.getCode()) == null) ? ""
368    : pdbJsonDoc.get(field.getCode()).toString();
369  0 if (field.isPrimaryKeyColumn())
370    {
371  0 primaryKey = fieldData;
372  0 summaryRowData[colCounter++] = primaryKey;
373    }
374  0 else if (fieldData == null || fieldData.isEmpty())
375    {
376  0 summaryRowData[colCounter++] = null;
377    }
378    else
379    {
380  0 try
381    {
382  0 summaryRowData[colCounter++] = (field.getDataType()
383    .getDataTypeClass() == Integer.class)
384    ? Integer.valueOf(fieldData)
385  0 : (field.getDataType()
386    .getDataTypeClass() == Double.class)
387    ? Double.valueOf(fieldData)
388    : sanitiseData(fieldData);
389    } catch (Exception e)
390    {
391  0 e.printStackTrace();
392  0 System.out.println("offending value:" + fieldData);
393    }
394    }
395    }
396   
397  0 final String primaryKey1 = primaryKey;
398   
399  0 final Object[] summaryRowData1 = summaryRowData;
400  0 return new FTSData()
401    {
 
402  0 toggle @Override
403    public Object[] getSummaryData()
404    {
405  0 return summaryRowData1;
406    }
407   
 
408  0 toggle @Override
409    public Object getPrimaryKey()
410    {
411  0 return primaryKey1;
412    }
413   
414    /**
415    * Returns a string representation of this object;
416    */
 
417  0 toggle @Override
418    public String toString()
419    {
420  0 StringBuilder summaryFieldValues = new StringBuilder();
421  0 for (Object summaryField : summaryRowData1)
422    {
423  0 summaryFieldValues.append(
424  0 summaryField == null ? " " : summaryField.toString())
425    .append("\t");
426    }
427  0 return summaryFieldValues.toString();
428    }
429   
430    /**
431    * Returns hash code value for this object
432    */
 
433  0 toggle @Override
434    public int hashCode()
435    {
436  0 return Objects.hash(primaryKey1, this.toString());
437    }
438   
 
439  0 toggle @Override
440    public boolean equals(Object that)
441    {
442  0 return this.toString().equals(that.toString());
443    }
444    };
445    }
446   
 
447  0 toggle private static String sanitiseData(String data)
448    {
449  0 String cleanData = data.replaceAll("\\[\"", "").replaceAll("\\]\"", "")
450    .replaceAll("\\[", "").replaceAll("\\]", "")
451    .replaceAll("\",\"", ", ").replaceAll("\"", "");
452  0 return cleanData;
453    }
454   
 
455  1 toggle @Override
456    public String getColumnDataConfigFileName()
457    {
458  1 return "/fts/pdb_data_columns.txt";
459    }
460   
 
461  19 toggle public static FTSRestClientI getInstance()
462    {
463  19 if (instance == null)
464    {
465  1 instance = new PDBFTSRestClient();
466    }
467  19 return instance;
468    }
469   
470    private Collection<FTSDataColumnI> allDefaultDisplayedStructureDataColumns;
471   
 
472  1 toggle public Collection<FTSDataColumnI> getAllDefaultDisplayedStructureDataColumns()
473    {
474  1 if (allDefaultDisplayedStructureDataColumns == null
475    || allDefaultDisplayedStructureDataColumns.isEmpty())
476    {
477  1 allDefaultDisplayedStructureDataColumns = new ArrayList<>();
478  1 allDefaultDisplayedStructureDataColumns
479    .addAll(super.getAllDefaultDisplayedFTSDataColumns());
480    }
481  1 return allDefaultDisplayedStructureDataColumns;
482    }
483   
484   
485    }