001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2008-2011 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * Sonar is free software; you can redistribute it and/or
007     * modify it under the terms of the GNU Lesser General Public
008     * License as published by the Free Software Foundation; either
009     * version 3 of the License, or (at your option) any later version.
010     *
011     * Sonar is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014     * Lesser General Public License for more details.
015     *
016     * You should have received a copy of the GNU Lesser General Public
017     * License along with Sonar; if not, write to the Free Software
018     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019     */
020    package org.sonar.wsclient.connectors;
021    
022    import java.io.BufferedReader;
023    import java.io.IOException;
024    import java.io.InputStream;
025    import java.io.InputStreamReader;
026    import java.io.UnsupportedEncodingException;
027    
028    import org.apache.commons.httpclient.Credentials;
029    import org.apache.commons.httpclient.HttpClient;
030    import org.apache.commons.httpclient.HttpException;
031    import org.apache.commons.httpclient.HttpMethod;
032    import org.apache.commons.httpclient.HttpMethodBase;
033    import org.apache.commons.httpclient.HttpStatus;
034    import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
035    import org.apache.commons.httpclient.UsernamePasswordCredentials;
036    import org.apache.commons.httpclient.auth.AuthScope;
037    import org.apache.commons.httpclient.methods.DeleteMethod;
038    import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
039    import org.apache.commons.httpclient.methods.GetMethod;
040    import org.apache.commons.httpclient.methods.PostMethod;
041    import org.apache.commons.httpclient.methods.PutMethod;
042    import org.apache.commons.httpclient.methods.StringRequestEntity;
043    import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
044    import org.sonar.wsclient.Host;
045    import org.sonar.wsclient.services.AbstractQuery;
046    import org.sonar.wsclient.services.CreateQuery;
047    import org.sonar.wsclient.services.DeleteQuery;
048    import org.sonar.wsclient.services.Query;
049    import org.sonar.wsclient.services.UpdateQuery;
050    
051    /**
052     * @since 2.1
053     */
054    public class HttpClient3Connector extends Connector {
055    
056      private static final int MAX_TOTAL_CONNECTIONS = 40;
057      private static final int MAX_HOST_CONNECTIONS = 4;
058    
059      private final Host server;
060      private HttpClient httpClient;
061    
062      public HttpClient3Connector(final Host server) {
063        this.server = server;
064        createClient();
065      }
066    
067      public HttpClient3Connector(Host server, HttpClient httpClient) {
068        this.httpClient = httpClient;
069        this.server = server;
070      }
071    
072      private void createClient() {
073        final HttpConnectionManagerParams params = new HttpConnectionManagerParams();
074        params.setConnectionTimeout(TIMEOUT_MS);
075        params.setSoTimeout(TIMEOUT_MS);
076        params.setDefaultMaxConnectionsPerHost(MAX_HOST_CONNECTIONS);
077        params.setMaxTotalConnections(MAX_TOTAL_CONNECTIONS);
078        final MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
079        connectionManager.setParams(params);
080        this.httpClient = new HttpClient(connectionManager);
081        configureCredentials();
082      }
083    
084      private void configureCredentials() {
085        if (server.getUsername() != null) {
086          httpClient.getParams().setAuthenticationPreemptive(true);
087          Credentials defaultcreds = new UsernamePasswordCredentials(server.getUsername(), server.getPassword());
088          httpClient.getState().setCredentials(AuthScope.ANY, defaultcreds);
089        }
090      }
091    
092      public String execute(Query<?> query) {
093        return executeRequest(newGetRequest(query));
094      }
095    
096      public String execute(CreateQuery<?> query) {
097        return executeRequest(newPostRequest(query));
098      }
099    
100      public String execute(UpdateQuery<?> query) {
101        return executeRequest(newPutRequest(query));
102      }
103    
104      public String execute(DeleteQuery<?> query) {
105        return executeRequest(newDeleteRequest(query));
106      }
107    
108      private String executeRequest(HttpMethodBase method) {
109        String json = null;
110        try {
111          httpClient.executeMethod(method);
112          if (method.getStatusCode() == HttpStatus.SC_OK) {
113            json = getResponseBodyAsString(method);
114    
115          } else if (method.getStatusCode() != HttpStatus.SC_NOT_FOUND) {
116            throw new ConnectionException("HTTP error: " + method.getStatusCode() + ", msg: " + method.getStatusText() + ", query: " + method);
117          }
118    
119        } catch (HttpException e) {
120          throw new ConnectionException("Query: " + method, e);
121    
122        } catch (IOException e) {
123          throw new ConnectionException("Query: " + method, e);
124    
125        } finally {
126          if (method != null) {
127            method.releaseConnection();
128          }
129        }
130        return json;
131      }
132    
133      private HttpMethodBase newGetRequest(Query<?> query) {
134        HttpMethodBase method = new GetMethod(server.getHost() + query.getUrl());
135        setJsonHeader(method);
136        return method;
137      }
138    
139      private HttpMethodBase newDeleteRequest(DeleteQuery<?> query) {
140        HttpMethodBase method = new DeleteMethod(server.getHost() + query.getUrl());
141        setJsonHeader(method);
142        return method;
143      }
144    
145      private HttpMethodBase newPostRequest(CreateQuery<?> query) {
146        PostMethod method = new PostMethod(server.getHost() + query.getUrl());
147        setJsonHeader(method);
148        setRequestEntity(method, query);
149        return method;
150      }
151    
152      private HttpMethodBase newPutRequest(UpdateQuery<?> query) {
153        PutMethod method = new PutMethod(server.getHost() + query.getUrl());
154        setJsonHeader(method);
155        setRequestEntity(method, query);
156        return method;
157      }
158    
159      private void setRequestEntity(EntityEnclosingMethod request, AbstractQuery<?> query) {
160        if (query.getBody() != null) {
161          try {
162            request.setRequestEntity(new StringRequestEntity(query.getBody(), "text/plain; charset=UTF-8", "UTF-8"));
163          } catch (UnsupportedEncodingException e) {
164            throw new ConnectionException("Unsupported encoding", e);
165          }
166        }
167      }
168    
169      private void setJsonHeader(HttpMethodBase request) {
170        request.setRequestHeader("Accept", "application/json");
171      }
172    
173      private String getResponseBodyAsString(HttpMethod method) {
174        BufferedReader reader = null;
175        try {
176          final InputStream inputStream = method.getResponseBodyAsStream();
177          reader = new BufferedReader(new InputStreamReader(inputStream));
178          final StringBuilder sb = new StringBuilder();
179          String line;
180    
181          while ((line = reader.readLine()) != null) {
182            sb.append(line).append("\n");
183          }
184          return sb.toString();
185    
186        } catch (IOException e) {
187          throw new ConnectionException("Can not read response", e);
188    
189        } finally {
190          if (reader != null) {
191            try {
192              reader.close();
193    
194            } catch (Exception e) {
195              // TODO
196            }
197          }
198        }
199      }
200    }