EMMA Coverage Report (generated Wed Oct 26 13:47:17 EDT 2011)
[all classes][org.jclouds.http.internal]

COVERAGE SUMMARY FOR SOURCE FILE [JavaUrlHttpCommandExecutorService.java]

nameclass, %method, %block, %line, %
JavaUrlHttpCommandExecutorService.java50%  (1/2)75%  (6/8)47%  (260/548)50%  (59.8/119)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class JavaUrlHttpCommandExecutorService$10%   (0/1)0%   (0/2)0%   (0/19)0%   (0/2)
JavaUrlHttpCommandExecutorService$1 (JavaUrlHttpCommandExecutorService): void 0%   (0/1)0%   (0/6)0%   (0/1)
getPasswordAuthentication (): PasswordAuthentication 0%   (0/1)0%   (0/13)0%   (0/1)
     
class JavaUrlHttpCommandExecutorService100% (1/1)100% (6/6)49%  (260/529)51%  (59.8/118)
convert (HttpRequest): HttpURLConnection 100% (1/1)28%  (94/336)29%  (20.6/71)
bufferAndCloseStream (InputStream): InputStream 100% (1/1)75%  (15/20)90%  (5.4/6)
cleanup (HttpURLConnection): void 100% (1/1)75%  (6/8)67%  (2/3)
invoke (HttpURLConnection): HttpResponse 100% (1/1)82%  (84/102)77%  (20.9/27)
<static initializer> 100% (1/1)89%  (16/18)94%  (1.9/2)
JavaUrlHttpCommandExecutorService (HttpUtils, ExecutorService, DelegatingRetr... 100% (1/1)100% (45/45)100% (9/9)

1/**
2 * Licensed to jclouds, Inc. (jclouds) under one or more
3 * contributor license agreements.  See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership.  jclouds licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License.  You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied.  See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.jclouds.http.internal;
20 
21import static com.google.common.base.Preconditions.checkArgument;
22import static com.google.common.base.Preconditions.checkNotNull;
23import static com.google.common.base.Throwables.propagate;
24import static com.google.common.collect.Iterables.getLast;
25import static com.google.common.io.ByteStreams.toByteArray;
26import static com.google.common.io.Closeables.closeQuietly;
27import static org.jclouds.io.Payloads.newInputStreamPayload;
28 
29import java.io.ByteArrayInputStream;
30import java.io.IOException;
31import java.io.InputStream;
32import java.lang.reflect.Field;
33import java.net.Authenticator;
34import java.net.HttpURLConnection;
35import java.net.InetSocketAddress;
36import java.net.PasswordAuthentication;
37import java.net.ProtocolException;
38import java.net.Proxy;
39import java.net.ProxySelector;
40import java.net.SocketAddress;
41import java.net.URL;
42import java.util.concurrent.ExecutorService;
43 
44import javax.annotation.Resource;
45import javax.inject.Inject;
46import javax.inject.Named;
47import javax.inject.Singleton;
48import javax.net.ssl.HostnameVerifier;
49import javax.net.ssl.HttpsURLConnection;
50import javax.net.ssl.SSLContext;
51import javax.ws.rs.core.HttpHeaders;
52 
53import org.jclouds.Constants;
54import org.jclouds.crypto.CryptoStreams;
55import org.jclouds.http.HttpCommandExecutorService;
56import org.jclouds.http.HttpRequest;
57import org.jclouds.http.HttpResponse;
58import org.jclouds.http.HttpUtils;
59import org.jclouds.http.IOExceptionRetryHandler;
60import org.jclouds.http.handlers.DelegatingErrorHandler;
61import org.jclouds.http.handlers.DelegatingRetryHandler;
62import org.jclouds.io.MutableContentMetadata;
63import org.jclouds.io.Payload;
64import org.jclouds.logging.Logger;
65import org.jclouds.rest.internal.RestAnnotationProcessor;
66 
67import com.google.common.base.Supplier;
68import com.google.common.collect.ImmutableMultimap;
69import com.google.common.collect.ImmutableMultimap.Builder;
70import com.google.common.io.CountingOutputStream;
71 
72/**
73 * Basic implementation of a {@link HttpCommandExecutorService}.
74 * 
75 * @author Adrian Cole
76 */
77@Singleton
78public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorService<HttpURLConnection> {
79 
80   public static final String USER_AGENT = "jclouds/1.0 java/" + System.getProperty("java.version");
81   @Resource
82   protected Logger logger = Logger.NULL;
83   private final Supplier<SSLContext> untrustedSSLContextProvider;
84   private final HostnameVerifier verifier;
85   private final Field methodField;
86 
87   @Inject
88   public JavaUrlHttpCommandExecutorService(HttpUtils utils,
89            @Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioWorkerExecutor,
90            DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
91            DelegatingErrorHandler errorHandler, HttpWire wire, @Named("untrusted") HostnameVerifier verifier,
92            @Named("untrusted") Supplier<SSLContext> untrustedSSLContextProvider) throws SecurityException,
93            NoSuchFieldException {
94      super(utils, ioWorkerExecutor, retryHandler, ioRetryHandler, errorHandler, wire);
95      if (utils.getMaxConnections() > 0)
96         System.setProperty("http.maxConnections", String.valueOf(checkNotNull(utils, "utils").getMaxConnections()));
97      this.untrustedSSLContextProvider = checkNotNull(untrustedSSLContextProvider, "untrustedSSLContextProvider");
98      this.verifier = checkNotNull(verifier, "verifier");
99      this.methodField = HttpURLConnection.class.getDeclaredField("method");
100      methodField.setAccessible(true);
101   }
102 
103   @Override
104   protected HttpResponse invoke(HttpURLConnection connection) throws IOException, InterruptedException {
105      HttpResponse.Builder builder = HttpResponse.builder();
106      InputStream in = null;
107      try {
108         in = consumeOnClose(connection.getInputStream());
109      } catch (IOException e) {
110         in = bufferAndCloseStream(connection.getErrorStream());
111      } catch (RuntimeException e) {
112         closeQuietly(in);
113         propagate(e);
114         assert false : "should have propagated exception";
115      }
116 
117      int responseCode = connection.getResponseCode();
118      if (responseCode == 204) {
119         closeQuietly(in);
120         in = null;
121      }
122      builder.statusCode(responseCode);
123      builder.message(connection.getResponseMessage());
124 
125      Builder<String, String> headerBuilder = ImmutableMultimap.<String, String> builder();
126      for (String header : connection.getHeaderFields().keySet()) {
127         // HTTP message comes back as a header without a key
128         if (header != null)
129            headerBuilder.putAll(header, connection.getHeaderFields().get(header));
130      }
131      ImmutableMultimap<String, String> headers = headerBuilder.build();
132      Payload payload = in != null ? newInputStreamPayload(in) : null;
133      if (payload != null) {
134         payload.getContentMetadata().setPropertiesFromHttpHeaders(headers);
135         builder.payload(payload);
136      }
137      builder.headers(RestAnnotationProcessor.filterOutContentHeaders(headers));
138      return builder.build();
139   }
140 
141   private InputStream bufferAndCloseStream(InputStream inputStream) throws IOException {
142      InputStream in = null;
143      try {
144         if (inputStream != null) {
145            in = new ByteArrayInputStream(toByteArray(inputStream));
146         }
147      } finally {
148         closeQuietly(inputStream);
149      }
150      return in;
151   }
152 
153   @Override
154   protected HttpURLConnection convert(HttpRequest request) throws IOException, InterruptedException {
155      boolean chunked = "chunked".equals(request.getFirstHeaderOrNull("Transfer-Encoding"));
156      URL url = request.getEndpoint().toURL();
157 
158      HttpURLConnection connection;
159 
160      if (utils.useSystemProxies()) {
161         System.setProperty("java.net.useSystemProxies", "true");
162         Iterable<Proxy> proxies = ProxySelector.getDefault().select(request.getEndpoint());
163         Proxy proxy = getLast(proxies);
164         connection = (HttpURLConnection) url.openConnection(proxy);
165      } else if (utils.getProxyHost() != null) {
166         SocketAddress addr = new InetSocketAddress(utils.getProxyHost(), utils.getProxyPort());
167         Proxy proxy = new Proxy(Proxy.Type.HTTP, addr);
168         Authenticator authenticator = new Authenticator() {
169            public PasswordAuthentication getPasswordAuthentication() {
170               return (new PasswordAuthentication(utils.getProxyUser(), utils.getProxyPassword().toCharArray()));
171            }
172         };
173         Authenticator.setDefault(authenticator);
174         connection = (HttpURLConnection) url.openConnection(proxy);
175      } else {
176         connection = (HttpURLConnection) url.openConnection();
177      }
178      if (connection instanceof HttpsURLConnection) {
179         HttpsURLConnection sslCon = (HttpsURLConnection) connection;
180         if (utils.relaxHostname())
181            sslCon.setHostnameVerifier(verifier);
182         if (utils.trustAllCerts())
183            sslCon.setSSLSocketFactory(untrustedSSLContextProvider.get().getSocketFactory());
184      }
185      if (utils.getConnectionTimeout() > 0) {
186         connection.setConnectTimeout(utils.getConnectionTimeout());
187      }
188      if (utils.getSocketOpenTimeout() > 0) {
189         connection.setReadTimeout(utils.getSocketOpenTimeout());
190      }
191      connection.setDoOutput(true);
192      connection.setAllowUserInteraction(false);
193      // do not follow redirects since https redirects don't work properly
194      // ex. Caused by: java.io.IOException: HTTPS hostname wrong: should be
195      // <adriancole.s3int0.s3-external-3.amazonaws.com>
196      connection.setInstanceFollowRedirects(false);
197      try {
198         connection.setRequestMethod(request.getMethod());
199      } catch (ProtocolException e) {
200         try {
201            methodField.set(connection, request.getMethod());
202         } catch (Exception e1) {
203            logger.error(e, "could not set request method: ", request.getMethod());
204            propagate(e1);
205         }
206      }
207 
208      for (String header : request.getHeaders().keys()) {
209         for (String value : request.getHeaders().get(header)) {
210            connection.setRequestProperty(header, value);
211         }
212      }
213      connection.setRequestProperty(HttpHeaders.HOST, request.getEndpoint().getHost());
214      connection.setRequestProperty(HttpHeaders.USER_AGENT, USER_AGENT);
215 
216      if (request.getPayload() != null) {
217         MutableContentMetadata md = request.getPayload().getContentMetadata();
218         if (md.getContentMD5() != null)
219            connection.setRequestProperty("Content-MD5", CryptoStreams.base64(md.getContentMD5()));
220         if (md.getContentType() != null)
221            connection.setRequestProperty(HttpHeaders.CONTENT_TYPE, md.getContentType());
222         if (md.getContentDisposition() != null)
223            connection.setRequestProperty("Content-Disposition", md.getContentDisposition());
224         if (md.getContentEncoding() != null)
225            connection.setRequestProperty("Content-Encoding", md.getContentEncoding());
226         if (md.getContentLanguage() != null)
227            connection.setRequestProperty("Content-Language", md.getContentLanguage());
228         if (chunked) {
229            connection.setChunkedStreamingMode(8196);
230         } else {
231            Long length = checkNotNull(md.getContentLength(), "payload.getContentLength");
232            connection.setRequestProperty(HttpHeaders.CONTENT_LENGTH, length.toString());
233            // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6755625
234            checkArgument(length < Integer.MAX_VALUE,
235                     "JDK 1.6 does not support >2GB chunks. Use chunked encoding, if possible.");
236            connection.setFixedLengthStreamingMode(length.intValue());
237            if (length.intValue() > 0) {
238               connection.setRequestProperty("Expect", "100-continue");
239            }
240         }
241         CountingOutputStream out = new CountingOutputStream(connection.getOutputStream());
242         try {
243            request.getPayload().writeTo(out);
244         } catch (IOException e) {
245            throw new RuntimeException(String.format("error after writing %d/%s bytes to %s", out.getCount(), md
246                     .getContentLength(), request.getRequestLine()), e);
247         }
248      } else {
249         connection.setRequestProperty(HttpHeaders.CONTENT_LENGTH, "0");
250         // for some reason POST/PUT undoes the content length header above.
251         if (connection.getRequestMethod().equals("POST") || connection.getRequestMethod().equals("PUT"))
252            connection.setFixedLengthStreamingMode(0);
253      }
254      return connection;
255 
256   }
257 
258   /**
259    * Only disconnect if there is no content, as disconnecting will throw away unconsumed content.
260    */
261   @Override
262   protected void cleanup(HttpURLConnection connection) {
263      if (connection != null && connection.getContentLength() == 0)
264         connection.disconnect();
265   }
266 
267}

[all classes][org.jclouds.http.internal]
EMMA 2.0.5312 (C) Vladimir Roubtsov