001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2009 SonarSource SA
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 org.apache.commons.httpclient.HttpException;
023 import org.apache.http.*;
024 import org.apache.http.auth.*;
025 import org.apache.http.client.CredentialsProvider;
026 import org.apache.http.client.methods.HttpDelete;
027 import org.apache.http.client.methods.HttpGet;
028 import org.apache.http.client.methods.HttpPost;
029 import org.apache.http.client.methods.HttpRequestBase;
030 import org.apache.http.client.protocol.ClientContext;
031 import org.apache.http.impl.auth.BasicScheme;
032 import org.apache.http.impl.client.DefaultHttpClient;
033 import org.apache.http.protocol.BasicHttpContext;
034 import org.apache.http.protocol.ExecutionContext;
035 import org.apache.http.protocol.HttpContext;
036 import org.apache.http.util.EntityUtils;
037 import org.sonar.wsclient.Host;
038 import org.sonar.wsclient.services.CreateQuery;
039 import org.sonar.wsclient.services.DeleteQuery;
040 import org.sonar.wsclient.services.Query;
041
042 import java.io.IOException;
043
044 /**
045 * @since 2.1
046 */
047 public class HttpClient4Connector extends Connector {
048
049 private Host server;
050
051 public HttpClient4Connector(Host server) {
052 this.server = server;
053 }
054
055 public String execute(Query query) {
056 return executeRequest(newGetMethod(query));
057 }
058
059 public String execute(CreateQuery query) {
060 return executeRequest(newPostMethod(query));
061 }
062
063 public String execute(DeleteQuery query) {
064 return executeRequest(newDeleteMethod(query));
065 }
066
067 private String executeRequest(HttpRequestBase request) {
068 String json = null;
069 DefaultHttpClient client = createClient();
070 try {
071 BasicHttpContext context = createLocalContext(client);
072 HttpResponse response = client.execute(request, context);
073 HttpEntity entity = response.getEntity();
074 if (entity != null) {
075 if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
076 json = EntityUtils.toString(entity);
077
078 } else if (response.getStatusLine().getStatusCode() != HttpStatus.SC_NOT_FOUND) {
079 throw new ConnectionException("HTTP error: " + response.getStatusLine().getStatusCode() + ", msg: " + response.getStatusLine().getReasonPhrase() + ", query: " + request.toString());
080 }
081 }
082
083 } catch (HttpException e) {
084 throw new ConnectionException("Query: " + request.getURI(), e);
085
086 } catch (IOException e) {
087 throw new ConnectionException("Query: " + request.getURI(), e);
088
089 } finally {
090 client.getConnectionManager().shutdown();
091 }
092 return json;
093 }
094
095 private DefaultHttpClient createClient() {
096 DefaultHttpClient client = new DefaultHttpClient();
097 if (server.getUsername() != null) {
098 client.getCredentialsProvider().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(server.getUsername(), server.getPassword()));
099 }
100 return client;
101 }
102
103 private BasicHttpContext createLocalContext(DefaultHttpClient client) {
104 BasicHttpContext localcontext = new BasicHttpContext();
105
106 if (server.getUsername() != null) {
107 // Generate BASIC scheme object and stick it to the local
108 // execution context
109 BasicScheme basicAuth = new BasicScheme();
110 localcontext.setAttribute("preemptive-auth", basicAuth);
111
112 // Add as the first request interceptor
113 client.addRequestInterceptor(new PreemptiveAuth(), 0);
114 }
115 return localcontext;
116 }
117
118 private HttpGet newGetMethod(Query query) {
119 HttpGet get = new HttpGet(server.getHost() + query.getUrl());
120 setJsonHeader(get);
121 return get;
122 }
123
124 private HttpDelete newDeleteMethod(DeleteQuery query) {
125 HttpDelete delete = new HttpDelete(server.getHost() + query.getUrl());
126 setJsonHeader(delete);
127 return delete;
128 }
129
130 private HttpPost newPostMethod(CreateQuery query) {
131 HttpPost post = new HttpPost(server.getHost() + query.getUrl());
132 setJsonHeader(post);
133 return post;
134 }
135
136 private void setJsonHeader(HttpRequestBase request) {
137 request.setHeader("Accept", "application/json");
138 }
139
140 static final class PreemptiveAuth implements HttpRequestInterceptor {
141 public void process(
142 final HttpRequest request,
143 final HttpContext context) throws IOException {
144
145 AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);
146
147 // If no auth scheme avaialble yet, try to initialize it preemptively
148 if (authState.getAuthScheme() == null) {
149 AuthScheme authScheme = (AuthScheme) context.getAttribute("preemptive-auth");
150 CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(ClientContext.CREDS_PROVIDER);
151 HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
152 if (authScheme != null) {
153 Credentials creds = credsProvider.getCredentials(
154 new AuthScope(
155 targetHost.getHostName(),
156 targetHost.getPort()));
157 if (creds == null) {
158 throw new HttpException("No credentials for preemptive authentication");
159 }
160 authState.setAuthScheme(authScheme);
161 authState.setCredentials(creds);
162 }
163 }
164 }
165 }
166 }