001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021package org.biojava.nbio.ws.hmmer;
022
023import net.sf.json.JSONArray;
024import net.sf.json.JSONObject;
025import org.biojava.nbio.core.sequence.ProteinSequence;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028
029import java.io.*;
030import java.net.HttpURLConnection;
031import java.net.URL;
032import java.util.SortedSet;
033import java.util.TreeSet;
034
035
036/** 
037 * Makes remote calls to the HMMER web service at the EBI web site and returns Pfam domain annotations for an input protein sequence.
038 *
039 * @author Andreas Prlic
040 * @since 3.0.3
041 */
042public class RemoteHmmerScan implements HmmerScan {
043
044        private static final Logger LOGGER = LoggerFactory.getLogger(RemoteHmmerScan.class);
045        
046        public static final String HMMER_SERVICE = "https://www.ebi.ac.uk/Tools/hmmer/search/hmmscan";
047
048        public RemoteHmmerScan(){
049
050        }
051
052
053        @Override
054        public  SortedSet<HmmerResult> scan(ProteinSequence sequence) throws IOException {
055
056                URL url = new URL(HMMER_SERVICE);
057
058                return scan(sequence, url);
059
060        }
061
062        /** 
063         * Scans a protein sequence for Pfam profile matches.
064         *
065         * @param sequence
066         * @param serviceLocation
067         * @return
068         * @throws IOException
069         */
070        public SortedSet<HmmerResult> scan(ProteinSequence sequence, URL serviceLocation) throws IOException{
071
072                StringBuffer postContent = new StringBuffer();
073
074                postContent.append("hmmdb=pfam");
075
076
077                // by default hmmscan runs with the HMMER3 cut_ga parameter enabled, the "gathering threshold", which depends on
078                // the cutoffs defined in the underlying HMM files.
079                // to request a different cutoff by e-value this could be enabled:
080                //postContent.append("&E=1");
081
082
083                postContent.append("&seq=");
084                postContent.append(sequence.getSequenceAsString());
085
086
087                HttpURLConnection connection = (HttpURLConnection) serviceLocation.openConnection();
088                connection.setDoOutput(true);
089                connection.setDoInput(true);
090                connection.setConnectTimeout(15000); // 15 sec
091                connection.setInstanceFollowRedirects(false);
092                connection.setRequestMethod("POST");
093                connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
094
095                connection.setRequestProperty("Accept","application/json");
096
097                connection.setRequestProperty("Content-Length", "" +
098                                Integer.toString(postContent.toString().getBytes().length));
099
100                //Send request
101                DataOutputStream wr = new DataOutputStream (
102                                connection.getOutputStream ());
103                wr.write(postContent.toString().getBytes());
104                wr.flush ();
105                wr.close ();
106
107
108                //Now get the redirect URL
109                URL respUrl = new URL( connection.getHeaderField( "Location" ));
110
111                int responseCode = connection.getResponseCode();
112                if ( responseCode == 500){
113                        LOGGER.warn("Got 500 response code for URL {}. Response message: {}.", serviceLocation, connection.getResponseMessage());       
114                }
115
116                HttpURLConnection connection2 = (HttpURLConnection) respUrl.openConnection();
117                connection2.setRequestMethod("GET");
118                connection2.setRequestProperty("Accept", "application/json");
119                connection2.setConnectTimeout(60000); // 1 minute
120
121                //Get the response
122                BufferedReader in = new BufferedReader(
123                                new InputStreamReader(
124                                                connection2.getInputStream()));
125
126                String inputLine;
127
128                StringBuffer result = new StringBuffer();
129                while ((inputLine = in.readLine()) != null) {
130                        result.append(inputLine);
131                }
132
133                in.close();
134
135                // process the response and build up a container for the data.
136
137                SortedSet<HmmerResult> results = new TreeSet<HmmerResult>();
138                try {
139                        JSONObject json =  JSONObject.fromObject(result.toString());
140
141                        JSONObject hmresults = json.getJSONObject("results");
142
143
144                        JSONArray hits = hmresults.getJSONArray("hits");
145
146                        for(int i =0 ; i < hits.size() ; i++){
147                                JSONObject hit = hits.getJSONObject(i);
148
149                                HmmerResult hmmResult = new HmmerResult();
150
151                                Object dclO = hit.get("dcl");
152                                Integer dcl = -1;
153                                if ( dclO instanceof Long){
154                                        Long dclL = (Long) dclO;
155                                        dcl = dclL.intValue();
156                                } else if ( dclO instanceof Integer){
157                                        dcl = (Integer) dclO;
158                                }
159
160
161                                hmmResult.setAcc((String)hit.get("acc"));
162                                hmmResult.setDcl(dcl);
163                                hmmResult.setDesc((String)hit.get("desc"));
164                                hmmResult.setEvalue(Float.parseFloat((String)hit.get("evalue")));
165                                hmmResult.setName((String)hit.get("name"));
166                                hmmResult.setNdom((Integer)hit.get("ndom"));
167                                hmmResult.setNreported((Integer)hit.get("nreported"));
168                                hmmResult.setPvalue((Double)hit.get("pvalue"));
169                                hmmResult.setScore(Float.parseFloat((String)hit.get("score")));
170
171                                JSONArray hmmdomains = hit.getJSONArray("domains");
172
173                                SortedSet<HmmerDomain> domains = new TreeSet<HmmerDomain>();
174                                for ( int j= 0 ; j < hmmdomains.size() ; j++){
175                                        JSONObject d = hmmdomains.getJSONObject(j);
176                                        Integer is_included = getInteger(d.get("is_included"));
177                                        if ( is_included == 0) {
178                                                continue;
179                                        }
180
181
182                                        // this filters out multiple hits to the same clan
183                                        Integer outcompeted = getInteger(d.get("outcompeted"));
184                                        if ( outcompeted != null && outcompeted == 1) {
185                                                continue;
186                                        }
187
188                                        Integer significant = getInteger(d.get("significant"));
189
190                                        if (  significant != 1) {
191                                                continue;
192                                        }
193
194                                        HmmerDomain dom = new HmmerDomain();
195                                        dom.setAliLenth((Integer)d.get("aliL"));
196                                        dom.setHmmAcc((String)d.get("alihmmacc"));
197                                        dom.setHmmDesc((String)d.get("alihmmdesc"));
198
199                                        dom.setHmmFrom(getInteger(d.get("alihmmfrom")));
200                                        dom.setHmmTo(getInteger(d.get("alihmmto")));
201                                        dom.setSimCount((Integer) d.get("aliSimCount"));
202                                        dom.setSqFrom(getInteger(d.get("alisqfrom")));
203                                        dom.setSqTo(getInteger(d.get("alisqto")));
204                                        dom.setHmmName((String)d.get("alihmmname"));
205                                        dom.setEvalue(Float.parseFloat((String)d.get("ievalue")));
206
207                                        domains.add(dom);
208
209
210                                }
211
212                                hmmResult.setDomains(domains);
213
214                                results.add(hmmResult);
215                        }
216                } catch (NumberFormatException e){
217                        LOGGER.warn("Could not parse number in Hmmer web service json response: {}", e.getMessage());
218                }
219
220                return results;
221
222        }
223
224
225        private Integer getInteger(Object object) {
226                if ( object instanceof Integer)
227                        return (Integer) object;
228                else if( object instanceof String)
229                        return Integer.parseInt((String) object);
230
231                return null;
232        }
233
234}