2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
21 package jalview.fts.service.pdb;
23 import jalview.datamodel.SequenceI;
24 import jalview.fts.api.FTSData;
25 import jalview.fts.api.FTSDataColumnI;
26 import jalview.fts.api.FTSRestClientI;
27 import jalview.fts.core.FTSRestClient;
28 import jalview.fts.core.FTSRestRequest;
29 import jalview.fts.core.FTSRestResponse;
30 import jalview.util.MessageManager;
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.Objects;
39 import javax.ws.rs.core.MediaType;
41 import org.json.simple.JSONArray;
42 import org.json.simple.JSONObject;
43 import org.json.simple.parser.JSONParser;
44 import org.json.simple.parser.ParseException;
46 //import jalview.javascript.web.Client;
47 //import jalview.javascript.web.ClientResponse;
48 //import jalview.javascript.web.WebResource;
50 import com.sun.jersey.api.client.Client;
51 import com.sun.jersey.api.client.ClientResponse;
52 import com.sun.jersey.api.client.WebResource;
54 import com.sun.jersey.api.client.config.ClientConfig;
55 import com.sun.jersey.api.client.config.DefaultClientConfig;
58 * A rest client for querying the Search endpoint of the PDB API
60 * BH 2018: just a tiny tweak here - for SwingJS, we coerce the classes to be
61 * from jalview.javascript.web instead of com.sun.jersey.api.client
67 public class PDBFTSRestClient extends FTSRestClient
70 private static FTSRestClientI instance = null;
72 public static final String PDB_SEARCH_ENDPOINT = "https://www.ebi.ac.uk/pdbe/search/pdb/select?";
74 protected PDBFTSRestClient()
79 * Takes a PDBRestRequest object and returns a response upon execution
81 * @param pdbRestRequest
82 * the PDBRestRequest instance to be processed
83 * @return the pdbResponse object for the given request
86 @SuppressWarnings("unused")
88 public FTSRestResponse executeRequest(FTSRestRequest pdbRestRequest)
94 WebResource webResource;
96 String wantedFields = getDataColumnsFieldsAsCommaDelimitedString(
97 pdbRestRequest.getWantedFields());
98 int responseSize = (pdbRestRequest.getResponseSize() == 0)
99 ? getDefaultResponsePageSize()
100 : pdbRestRequest.getResponseSize();
101 int offSet = pdbRestRequest.getOffSet();
102 String sortParam = null;
103 if (pdbRestRequest.getFieldToSortBy() == null
104 || pdbRestRequest.getFieldToSortBy().trim().isEmpty())
110 if (pdbRestRequest.getFieldToSortBy()
111 .equalsIgnoreCase("Resolution"))
113 sortParam = pdbRestRequest.getFieldToSortBy()
114 + (pdbRestRequest.isAscending() ? " asc" : " desc");
118 sortParam = pdbRestRequest.getFieldToSortBy()
119 + (pdbRestRequest.isAscending() ? " desc" : " asc");
123 String facetPivot = (pdbRestRequest.getFacetPivot() == null
124 || pdbRestRequest.getFacetPivot().isEmpty()) ? ""
125 : pdbRestRequest.getFacetPivot();
126 String facetPivotMinCount = String
127 .valueOf(pdbRestRequest.getFacetPivotMinCount());
129 String query = pdbRestRequest.getFieldToSearchBy()
130 + pdbRestRequest.getSearchTerm()
131 + (pdbRestRequest.isAllowEmptySeq() ? ""
132 : " AND molecule_sequence:['' TO *]")
133 + (pdbRestRequest.isAllowUnpublishedEntries() ? ""
134 : " AND status:REL");
136 if (/** @j2sNative true || */
139 client = (Client) (Object) new jalview.javascript.web.Client();
143 // Build request parameters for the REST Request
144 ClientConfig clientConfig = new DefaultClientConfig();
145 client = Client.create(clientConfig);
148 if (pdbRestRequest.isFacet())
150 webResource = client.resource(PDB_SEARCH_ENDPOINT)
151 .queryParam("wt", "json").queryParam("fl", wantedFields)
152 .queryParam("rows", String.valueOf(responseSize))
153 .queryParam("q", query)
154 .queryParam("start", String.valueOf(offSet))
155 .queryParam("sort", sortParam).queryParam("facet", "true")
156 .queryParam("facet.pivot", facetPivot)
157 .queryParam("facet.pivot.mincount", facetPivotMinCount);
161 webResource = client.resource(PDB_SEARCH_ENDPOINT)
162 .queryParam("wt", "json").queryParam("fl", wantedFields)
163 .queryParam("rows", String.valueOf(responseSize))
164 .queryParam("start", String.valueOf(offSet))
165 .queryParam("q", query).queryParam("sort", sortParam);
168 URI uri = webResource.getURI();
170 // Execute the REST request
171 ClientResponse clientResponse = webResource
172 .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
174 // Get the JSON string from the response object
175 String responseString = clientResponse.getEntity(String.class);
176 // System.out.println("query >>>>>>> " + pdbRestRequest.toString());
178 // Check the response status and report exception if one occurs
179 if (clientResponse.getStatus() != 200)
181 String errorMessage = "";
182 if (clientResponse.getStatus() == 400)
184 errorMessage = parseJsonExceptionString(responseString);
185 throw new Exception(errorMessage);
189 errorMessage = getMessageByHTTPStatusCode(
190 clientResponse.getStatus(), "PDB");
191 throw new Exception(errorMessage);
195 // Make redundant objects eligible for garbage collection to conserve
197 clientResponse = null;
200 // Process the response and return the result to the caller.
201 return parsePDBJsonResponse(responseString, pdbRestRequest);
202 } catch (Exception e)
204 String exceptionMsg = e.getMessage();
205 if (exceptionMsg.contains("SocketException"))
207 // No internet connection
208 throw new Exception(MessageManager.getString(
209 "exception.unable_to_detect_internet_connection"));
211 else if (exceptionMsg.contains("UnknownHostException"))
213 // The server 'www.ebi.ac.uk' is unreachable
214 throw new Exception(MessageManager.formatMessage(
215 "exception.fts_server_unreachable", "PDB Solr"));
225 * Process error response from PDB server if/when one occurs.
227 * @param jsonResponse
228 * the JSON string containing error message from the server
229 * @return the processed error message from the JSON string
231 public static String parseJsonExceptionString(String jsonErrorResponse)
233 StringBuilder errorMessage = new StringBuilder(
234 "\n============= PDB Rest Client RunTime error =============\n");
238 // "responseHeader":{
242 // "q":"(text:q93xj9_soltu) AND molecule_sequence:['' TO *] AND status:REL",
243 // "fl":"pdb_id,title,experimental_method,resolution",
245 // "sort":"overall_quality desc",
248 // "response":{"numFound":1,"start":0,"docs":[
250 // "experimental_method":["X-ray diffraction"],
252 // "resolution":2.46,
253 // "title":"The crystal structure of Potato ferredoxin I with 2Fe-2S cluster"}]
258 JSONParser jsonParser = new JSONParser();
259 JSONObject jsonObj = (JSONObject) jsonParser.parse(jsonErrorResponse);
260 JSONObject errorResponse = (JSONObject) jsonObj.get("error");
262 JSONObject responseHeader = (JSONObject) jsonObj
263 .get("responseHeader");
264 JSONObject paramsObj = (JSONObject) responseHeader.get("params");
265 String status = responseHeader.get("status").toString();
266 String message = errorResponse.get("msg").toString();
267 String query = paramsObj.get("q").toString();
268 String fl = paramsObj.get("fl").toString();
270 errorMessage.append("Status: ").append(status).append("\n");
271 errorMessage.append("Message: ").append(message).append("\n");
272 errorMessage.append("query: ").append(query).append("\n");
273 errorMessage.append("fl: ").append(fl).append("\n");
275 } catch (ParseException e)
279 return errorMessage.toString();
283 * Parses the JSON response string from PDB REST API. The response is dynamic
284 * hence, only fields specifically requested for in the 'wantedFields'
285 * parameter is fetched/processed
287 * @param pdbJsonResponseString
288 * the JSON string to be parsed
289 * @param pdbRestRequest
290 * the request object which contains parameters used to process the
294 @SuppressWarnings("unchecked")
295 public static FTSRestResponse parsePDBJsonResponse(
296 String pdbJsonResponseString, FTSRestRequest pdbRestRequest)
298 FTSRestResponse searchResult = new FTSRestResponse();
299 List<FTSData> result = null;
302 JSONParser jsonParser = new JSONParser();
303 JSONObject jsonObj = (JSONObject) jsonParser
304 .parse(pdbJsonResponseString);
306 JSONObject pdbResponse = (JSONObject) jsonObj.get("response");
307 String queryTime = ((JSONObject) jsonObj.get("responseHeader"))
308 .get("QTime").toString();
309 int numFound = Integer
310 .valueOf(pdbResponse.get("numFound").toString());
313 result = new ArrayList<FTSData>();
314 JSONArray docs = (JSONArray) pdbResponse.get("docs");
315 for (Iterator<JSONObject> docIter = docs.iterator(); docIter
318 JSONObject doc = docIter.next();
319 result.add(getFTSData(doc, pdbRestRequest));
321 searchResult.setNumberOfItemsFound(numFound);
322 searchResult.setResponseTime(queryTime);
323 searchResult.setSearchSummary(result);
325 } catch (ParseException e)
332 public static FTSData getFTSData(JSONObject pdbJsonDoc,
333 FTSRestRequest request)
336 String primaryKey = null;
338 Object[] summaryRowData;
340 SequenceI associatedSequence;
342 Collection<FTSDataColumnI> diplayFields = request.getWantedFields();
343 SequenceI associatedSeq = request.getAssociatedSequence();
345 summaryRowData = new Object[(associatedSeq != null)
346 ? diplayFields.size() + 1
347 : diplayFields.size()];
348 if (associatedSeq != null)
350 associatedSequence = associatedSeq;
351 summaryRowData[0] = associatedSequence;
355 for (FTSDataColumnI field : diplayFields)
357 String fieldData = (pdbJsonDoc.get(field.getCode()) == null) ? ""
358 : pdbJsonDoc.get(field.getCode()).toString();
359 if (field.isPrimaryKeyColumn())
361 primaryKey = fieldData;
362 summaryRowData[colCounter++] = primaryKey;
364 else if (fieldData == null || fieldData.isEmpty())
366 summaryRowData[colCounter++] = null;
372 summaryRowData[colCounter++] = (field.getDataType()
373 .getDataTypeClass() == Integer.class)
374 ? Integer.valueOf(fieldData)
375 : (field.getDataType()
376 .getDataTypeClass() == Double.class)
377 ? Double.valueOf(fieldData)
378 : sanitiseData(fieldData);
379 } catch (Exception e)
382 System.out.println("offending value:" + fieldData);
387 final String primaryKey1 = primaryKey;
389 final Object[] summaryRowData1 = summaryRowData;
393 public Object[] getSummaryData()
395 return summaryRowData1;
399 public Object getPrimaryKey()
405 * Returns a string representation of this object;
408 public String toString()
410 StringBuilder summaryFieldValues = new StringBuilder();
411 for (Object summaryField : summaryRowData1)
413 summaryFieldValues.append(
414 summaryField == null ? " " : summaryField.toString())
417 return summaryFieldValues.toString();
421 * Returns hash code value for this object
424 public int hashCode()
426 return Objects.hash(primaryKey1, this.toString());
430 public boolean equals(Object that)
432 return this.toString().equals(that.toString());
437 private static String sanitiseData(String data)
439 String cleanData = data.replaceAll("\\[\"", "").replaceAll("\\]\"", "")
440 .replaceAll("\\[", "").replaceAll("\\]", "")
441 .replaceAll("\",\"", ", ").replaceAll("\"", "");
446 public String getColumnDataConfigFileName()
448 return "/fts/pdb_data_columns.txt";
451 public static FTSRestClientI getInstance()
453 if (instance == null)
455 instance = new PDBFTSRestClient();
460 private Collection<FTSDataColumnI> allDefaultDisplayedStructureDataColumns;
462 public Collection<FTSDataColumnI> getAllDefaultDisplayedStructureDataColumns()
464 if (allDefaultDisplayedStructureDataColumns == null
465 || allDefaultDisplayedStructureDataColumns.isEmpty())
467 allDefaultDisplayedStructureDataColumns = new ArrayList<FTSDataColumnI>();
468 allDefaultDisplayedStructureDataColumns
469 .addAll(super.getAllDefaultDisplayedFTSDataColumns());
471 return allDefaultDisplayedStructureDataColumns;