JAL-2885 uniprot now https, uniprot/ensembl/pfam/xfam configurable
[jalview.git] / src / jalview / fts / service / uniprot / UniProtFTSRestClient.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
22 package jalview.fts.service.uniprot;
23
24 import jalview.bin.Cache;
25 import jalview.fts.api.FTSData;
26 import jalview.fts.api.FTSDataColumnI;
27 import jalview.fts.api.FTSRestClientI;
28 import jalview.fts.core.FTSRestClient;
29 import jalview.fts.core.FTSRestRequest;
30 import jalview.fts.core.FTSRestResponse;
31 import jalview.util.MessageManager;
32
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.List;
36 import java.util.Objects;
37
38 import javax.ws.rs.core.MediaType;
39
40 import com.sun.jersey.api.client.Client;
41 import com.sun.jersey.api.client.ClientResponse;
42 import com.sun.jersey.api.client.WebResource;
43 import com.sun.jersey.api.client.config.ClientConfig;
44 import com.sun.jersey.api.client.config.DefaultClientConfig;
45
46 public class UniProtFTSRestClient extends FTSRestClient
47 {
48   private static final String DEFAULT_UNIPROT_DOMAIN = "https://www.uniprot.org";
49
50   private static FTSRestClientI instance = null;
51
52   public final String uniprotSearchEndpoint;
53
54   public UniProtFTSRestClient()
55   {
56     super();
57     uniprotSearchEndpoint = Cache.getDefault("UNIPROT_DOMAIN",
58             DEFAULT_UNIPROT_DOMAIN) + "/uniprot/?";
59   }
60
61   @Override
62   public FTSRestResponse executeRequest(FTSRestRequest uniportRestRequest)
63           throws Exception
64   {
65     try
66     {
67       ClientConfig clientConfig = new DefaultClientConfig();
68       Client client = Client.create(clientConfig);
69
70       String wantedFields = getDataColumnsFieldsAsCommaDelimitedString(
71               uniportRestRequest.getWantedFields());
72       int responseSize = (uniportRestRequest.getResponseSize() == 0)
73               ? getDefaultResponsePageSize()
74               : uniportRestRequest.getResponseSize();
75
76       int offSet = uniportRestRequest.getOffSet();
77       String query;
78       if (isAdvancedQuery(uniportRestRequest.getSearchTerm()))
79       {
80         query = uniportRestRequest.getSearchTerm();
81       }
82       else
83       {
84         query = uniportRestRequest.getFieldToSearchBy()
85                 .equalsIgnoreCase("Search All")
86                         ? uniportRestRequest.getSearchTerm()
87                                 + " or mnemonic:"
88                                 + uniportRestRequest.getSearchTerm()
89                         : uniportRestRequest.getFieldToSearchBy() + ":"
90                                 + uniportRestRequest.getSearchTerm();
91       }
92
93       WebResource webResource = null;
94       webResource = client.resource(uniprotSearchEndpoint)
95               .queryParam("format", "tab")
96               .queryParam("columns", wantedFields)
97               .queryParam("limit", String.valueOf(responseSize))
98               .queryParam("offset", String.valueOf(offSet))
99               .queryParam("sort", "score").queryParam("query", query);
100       // Execute the REST request
101       ClientResponse clientResponse = webResource
102               .accept(MediaType.TEXT_PLAIN).get(ClientResponse.class);
103       String uniProtTabDelimittedResponseString = clientResponse
104               .getEntity(String.class);
105       // Make redundant objects eligible for garbage collection to conserve
106       // memory
107       // System.out.println(">>>>> response : "
108       // + uniProtTabDelimittedResponseString);
109       if (clientResponse.getStatus() != 200)
110       {
111         String errorMessage = getMessageByHTTPStatusCode(
112                 clientResponse.getStatus(), "Uniprot");
113         throw new Exception(errorMessage);
114
115       }
116       int xTotalResults = Integer.valueOf(
117               clientResponse.getHeaders().get("X-Total-Results").get(0));
118       clientResponse = null;
119       client = null;
120       return parseUniprotResponse(uniProtTabDelimittedResponseString,
121               uniportRestRequest, xTotalResults);
122     } catch (Exception e)
123     {
124       String exceptionMsg = e.getMessage();
125       if (exceptionMsg.contains("SocketException"))
126       {
127         // No internet connection
128         throw new Exception(MessageManager.getString(
129                 "exception.unable_to_detect_internet_connection"));
130       }
131       else if (exceptionMsg.contains("UnknownHostException"))
132       {
133         // The server 'http://www.uniprot.org' is unreachable
134         throw new Exception(MessageManager.formatMessage(
135                 "exception.fts_server_unreachable", "Uniprot"));
136       }
137       else
138       {
139         throw e;
140       }
141     }
142   }
143
144   public boolean isAdvancedQuery(String query)
145   {
146     if (query.contains(" AND ") || query.contains(" OR ")
147             || query.contains(" NOT ") || query.contains(" ! ")
148             || query.contains(" || ") || query.contains(" && ")
149             || query.contains(":") || query.contains("-"))
150     {
151       return true;
152     }
153     return false;
154   }
155
156   public FTSRestResponse parseUniprotResponse(
157           String uniProtTabDelimittedResponseString,
158           FTSRestRequest uniprotRestRequest, int xTotalResults)
159   {
160     FTSRestResponse searchResult = new FTSRestResponse();
161     List<FTSData> result = null;
162     if (uniProtTabDelimittedResponseString == null
163             || uniProtTabDelimittedResponseString.trim().isEmpty())
164     {
165       searchResult.setNumberOfItemsFound(0);
166       return searchResult;
167     }
168     String[] foundDataRow = uniProtTabDelimittedResponseString.split("\n");
169     if (foundDataRow != null && foundDataRow.length > 0)
170     {
171       result = new ArrayList<>();
172       boolean firstRow = true;
173       for (String dataRow : foundDataRow)
174       {
175         // The first data row is usually the header data. This should be
176         // filtered out from the rest of the data See: JAL-2485
177         if (firstRow)
178         {
179           firstRow = false;
180           continue;
181         }
182         // System.out.println(dataRow);
183         result.add(getFTSData(dataRow, uniprotRestRequest));
184       }
185       searchResult.setNumberOfItemsFound(xTotalResults);
186       searchResult.setSearchSummary(result);
187     }
188     return searchResult;
189   }
190
191   /**
192    * Takes a collection of FTSDataColumnI and converts its 'code' values into a
193    * tab delimited string.
194    * 
195    * @param dataColumnFields
196    *          the collection of FTSDataColumnI to process
197    * @return the generated comma delimited string from the supplied
198    *         FTSDataColumnI collection
199    */
200   private String getDataColumnsFieldsAsTabDelimitedString(
201           Collection<FTSDataColumnI> dataColumnFields)
202   {
203     String result = "";
204     if (dataColumnFields != null && !dataColumnFields.isEmpty())
205     {
206       StringBuilder returnedFields = new StringBuilder();
207       for (FTSDataColumnI field : dataColumnFields)
208       {
209         if (field.getName().equalsIgnoreCase("Uniprot Id"))
210         {
211           returnedFields.append("\t").append("Entry");
212         }
213         else
214         {
215           returnedFields.append("\t").append(field.getName());
216         }
217       }
218       returnedFields.deleteCharAt(0);
219       result = returnedFields.toString();
220     }
221     return result;
222   }
223
224   public static FTSData getFTSData(String tabDelimittedDataStr,
225           FTSRestRequest request)
226   {
227     String primaryKey = null;
228
229     Object[] summaryRowData;
230
231     Collection<FTSDataColumnI> diplayFields = request.getWantedFields();
232     int colCounter = 0;
233     summaryRowData = new Object[diplayFields.size()];
234     String[] columns = tabDelimittedDataStr.split("\t");
235     for (FTSDataColumnI field : diplayFields)
236     {
237       try
238       {
239         String fieldData = columns[colCounter];
240         if (field.isPrimaryKeyColumn())
241         {
242           primaryKey = fieldData;
243           summaryRowData[colCounter++] = primaryKey;
244         }
245         else if (fieldData == null || fieldData.isEmpty())
246         {
247           summaryRowData[colCounter++] = null;
248         }
249         else
250         {
251           try
252           {
253             summaryRowData[colCounter++] = (field.getDataType()
254                     .getDataTypeClass() == Integer.class)
255                             ? Integer.valueOf(fieldData.replace(",", ""))
256                             : (field.getDataType()
257                                     .getDataTypeClass() == Double.class)
258                                             ? Double.valueOf(fieldData)
259                                             : fieldData;
260           } catch (Exception e)
261           {
262             e.printStackTrace();
263             System.out.println("offending value:" + fieldData);
264           }
265         }
266       } catch (Exception e)
267       {
268         // e.printStackTrace();
269       }
270     }
271
272     final String primaryKey1 = primaryKey;
273
274     final Object[] summaryRowData1 = summaryRowData;
275     return new FTSData()
276     {
277       @Override
278       public Object[] getSummaryData()
279       {
280         return summaryRowData1;
281       }
282
283       @Override
284       public Object getPrimaryKey()
285       {
286         return primaryKey1;
287       }
288
289       /**
290        * Returns a string representation of this object;
291        */
292       @Override
293       public String toString()
294       {
295         StringBuilder summaryFieldValues = new StringBuilder();
296         for (Object summaryField : summaryRowData1)
297         {
298           summaryFieldValues.append(
299                   summaryField == null ? " " : summaryField.toString())
300                   .append("\t");
301         }
302         return summaryFieldValues.toString();
303       }
304
305       /**
306        * Returns hash code value for this object
307        */
308       @Override
309       public int hashCode()
310       {
311         return Objects.hash(primaryKey1, this.toString());
312       }
313
314       @Override
315       public boolean equals(Object that)
316       {
317         return this.toString().equals(that.toString());
318       }
319     };
320   }
321
322   public static FTSRestClientI getInstance()
323   {
324     if (instance == null)
325     {
326       instance = new UniProtFTSRestClient();
327     }
328     return instance;
329   }
330
331   @Override
332   public String getColumnDataConfigFileName()
333   {
334     return "/fts/uniprot_data_columns.txt";
335   }
336
337 }