001/*-------------------------------------------------------------------------+ 002| | 003| Copyright 2005-2011 the ConQAT Project | 004| | 005| Licensed under the Apache License, Version 2.0 (the "License"); | 006| you may not use this file except in compliance with the License. | 007| You may obtain a copy of the License at | 008| | 009| http://www.apache.org/licenses/LICENSE-2.0 | 010| | 011| Unless required by applicable law or agreed to in writing, software | 012| distributed under the License is distributed on an "AS IS" BASIS, | 013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 014| See the License for the specific language governing permissions and | 015| limitations under the License. | 016+-------------------------------------------------------------------------*/ 017package org.conqat.engine.service.shared.client; 018 019import java.io.File; 020import java.io.IOException; 021import java.net.URI; 022import java.security.KeyManagementException; 023import java.security.SecureRandom; 024import java.util.List; 025 026import javax.net.ssl.SSLContext; 027import javax.net.ssl.TrustManager; 028 029import org.apache.http.HttpEntity; 030import org.apache.http.HttpHeaders; 031import org.apache.http.HttpHost; 032import org.apache.http.HttpResponse; 033import org.apache.http.HttpStatus; 034import org.apache.http.auth.AuthScope; 035import org.apache.http.auth.UsernamePasswordCredentials; 036import org.apache.http.client.AuthCache; 037import org.apache.http.client.CredentialsProvider; 038import org.apache.http.client.HttpClient; 039import org.apache.http.client.config.RequestConfig; 040import org.apache.http.client.methods.HttpRequestBase; 041import org.apache.http.client.protocol.HttpClientContext; 042import org.apache.http.conn.ssl.SSLConnectionSocketFactory; 043import org.apache.http.entity.ContentType; 044import org.apache.http.entity.mime.FormBodyPart; 045import org.apache.http.entity.mime.FormBodyPartBuilder; 046import org.apache.http.entity.mime.MultipartEntityBuilder; 047import org.apache.http.entity.mime.content.ByteArrayBody; 048import org.apache.http.entity.mime.content.FileBody; 049import org.apache.http.impl.auth.BasicScheme; 050import org.apache.http.impl.client.BasicAuthCache; 051import org.apache.http.impl.client.BasicCredentialsProvider; 052import org.apache.http.impl.client.CloseableHttpClient; 053import org.apache.http.impl.client.HttpClients; 054import org.apache.http.ssl.SSLContexts; 055import org.apache.http.util.EntityUtils; 056import org.conqat.engine.service.shared.EMimeType; 057import org.conqat.lib.commons.collections.CollectionUtils; 058import org.conqat.lib.commons.net.TrustAllCertificatesManager; 059import org.conqat.lib.commons.resources.Resource; 060 061/** 062 * Provides utility methods for ServiceClient and IdeServiceClient. 063 */ 064public class ServiceClientUtils { 065 066 /** 067 * Sets up the HTTP client and returns it. The caller of this method is 068 * responsible for closing the client when it is no longer needed. 069 */ 070 public static CloseableHttpClient getHttpClient(ServerDetails serverDetails) throws ServiceCallException { 071 try { 072 SSLContext sslContext = SSLContexts.createDefault(); 073 sslContext.init(null, new TrustManager[] { new TrustAllCertificatesManager() }, new SecureRandom()); 074 SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext); 075 CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); 076 credentialsProvider.setCredentials(AuthScope.ANY, 077 new UsernamePasswordCredentials(serverDetails.getUsername(), serverDetails.getPassword())); 078 RequestConfig.Builder requestBuilder = RequestConfig.custom(); 079 int timeoutMilliseconds = serverDetails.getTimeoutSeconds() * 1000; 080 requestBuilder = requestBuilder.setConnectTimeout(timeoutMilliseconds); 081 requestBuilder = requestBuilder.setSocketTimeout(timeoutMilliseconds); 082 requestBuilder = requestBuilder.setConnectionRequestTimeout(timeoutMilliseconds); 083 return HttpClients.custom().setSSLSocketFactory(socketFactory) 084 .setDefaultRequestConfig(requestBuilder.build()).setDefaultCredentialsProvider(credentialsProvider) 085 .build(); 086 } catch (KeyManagementException e) { 087 throw new ServiceCallException("Error creating HTTP client: " + e.getMessage(), e); 088 } 089 } 090 091 /** 092 * Creates a HTTP context for preemptive basic authentication, i.e. without 093 * waiting for a server authentication request.</a>. 094 */ 095 public static HttpClientContext createPreemptiveAuthContext(HttpRequestBase request) { 096 URI uri = request.getURI(); 097 AuthCache authCache = new BasicAuthCache(); 098 authCache.put(new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme()), new BasicScheme()); 099 HttpClientContext context = HttpClientContext.create(); 100 context.setAuthCache(authCache); 101 return context; 102 } 103 104 /** 105 * Creates a multi-part entity that uploads the given files under the given 106 * request parameter. 107 */ 108 public static HttpEntity createMultiPartEntity(String parameterName, List<Resource> files) { 109 MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create(); 110 for (Resource file : files) { 111 FormBodyPart bodyPart = FormBodyPartBuilder.create(parameterName, 112 new ByteArrayBody(file.getAsByteArray(), ContentType.APPLICATION_OCTET_STREAM, file.getName())) 113 .build(); 114 multipartEntityBuilder.addPart(bodyPart); 115 } 116 return multipartEntityBuilder.build(); 117 } 118 119 /** 120 * Creates a multi-part entity that uploads the given files under the given 121 * request parameter. 122 */ 123 public static HttpEntity createMultiPartEntityForFiles(String parameterName, List<File> files) { 124 MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create(); 125 for (File file : files) { 126 FormBodyPart bodyPart = FormBodyPartBuilder 127 .create(parameterName, new FileBody(file, ContentType.APPLICATION_OCTET_STREAM)).build(); 128 multipartEntityBuilder.addPart(bodyPart); 129 } 130 return multipartEntityBuilder.build(); 131 } 132 133 /** 134 * Executes a request and handles the response. The request's header is not set 135 * to accept any specific type. Returns null in case of a "not found" HTTP 136 * return code. 137 */ 138 public static <T> T executeRequest(HttpClient client, HttpRequestBase request, EMimeType contentType, 139 CollectionUtils.FunctionWithException<String, T, IOException> deserializeFunction) 140 throws ServiceCallException { 141 if (contentType != null) { 142 request.setHeader(HttpHeaders.ACCEPT, contentType.getType()); 143 } 144 try { 145 HttpResponse response = client.execute(request, ServiceClientUtils.createPreemptiveAuthContext(request)); 146 int statusCode = response.getStatusLine().getStatusCode(); 147 148 switch (statusCode) { 149 case HttpStatus.SC_OK: 150 return deserializeFunction.apply(EntityUtils.toString(response.getEntity())); 151 case HttpStatus.SC_NO_CONTENT: 152 case HttpStatus.SC_NOT_FOUND: 153 return null; 154 default: 155 String responseBody = EntityUtils.toString(response.getEntity()); 156 throw new ServiceCallException( 157 "Service returned with status code " + statusCode + ". " 158 + response.getStatusLine().getReasonPhrase() + "\n" + responseBody, 159 statusCode, responseBody); 160 } 161 } catch (IOException | IllegalArgumentException e) { 162 // IllegalArgumentException raised when the server port is illegal. Note that 163 // this will also catch IllegalArgumentExceptions thrown for other reasons. 164 throw new ServiceCallException("Service call " + request.getURI() + " failed:" + e.getMessage(), e); 165 } 166 } 167}