Skip to content

Commit f66644b

Browse files
authored
Add support for apache http-client 4.5 (#234)
1 parent ae24342 commit f66644b

File tree

16 files changed

+683
-2
lines changed

16 files changed

+683
-2
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ env:
2121
- MODULE=http42
2222
- MODULE=http43
2323
- MODULE=http44
24+
- MODULE=http45
2425

2526
branches:
2627
except:

cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@ public class Cloudinary {
2323
"com.cloudinary.android.UploaderStrategy",
2424
"com.cloudinary.http42.UploaderStrategy",
2525
"com.cloudinary.http43.UploaderStrategy",
26-
"com.cloudinary.http44.UploaderStrategy"));
26+
"com.cloudinary.http44.UploaderStrategy",
27+
"com.cloudinary.http45.UploaderStrategy"));
2728
public static List<String> API_STRATEGIES = new ArrayList<String>(Arrays.asList(
2829
"com.cloudinary.android.ApiStrategy",
2930
"com.cloudinary.http42.ApiStrategy",
3031
"com.cloudinary.http43.ApiStrategy",
31-
"com.cloudinary.http44.ApiStrategy"));
32+
"com.cloudinary.http44.ApiStrategy",
33+
"com.cloudinary.http45.ApiStrategy"));
3234

3335
public final static String CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net";
3436
public final static String OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net";

cloudinary-http45/build.gradle

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
plugins {
2+
id 'java-library'
3+
id 'signing'
4+
id 'maven-publish'
5+
id 'io.codearte.nexus-staging' version '0.21.1'
6+
id "de.marcphilipp.nexus-publish" version "0.4.0"
7+
}
8+
9+
apply from: "../java_shared.gradle"
10+
11+
task ciTest( type: Test ) {
12+
useJUnit {
13+
excludeCategories 'com.cloudinary.test.TimeoutTest'
14+
}
15+
}
16+
17+
dependencies {
18+
compile project(':cloudinary-core')
19+
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.1'
20+
compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.13'
21+
compile group: 'org.apache.httpcomponents', name: 'httpmime', version: '4.5.13'
22+
testCompile project(':cloudinary-test-common')
23+
testCompile group: 'org.hamcrest', name: 'java-hamcrest', version: '2.0.0.0'
24+
testCompile group: 'pl.pragmatists', name: 'JUnitParams', version: '1.0.5'
25+
testCompile group: 'junit', name: 'junit', version: '4.12'
26+
}
27+
28+
if (hasProperty("ossrhPassword")) {
29+
30+
signing {
31+
sign configurations.archives
32+
}
33+
34+
nexusStaging {
35+
packageGroup = group
36+
username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : ""
37+
password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : ""
38+
}
39+
40+
publishing {
41+
publications {
42+
mavenJava(MavenPublication) {
43+
from components.java
44+
artifact sourcesJar
45+
artifact javadocJar
46+
pom {
47+
name = 'Cloudinary Apache HTTP 4.5 Library'
48+
packaging = 'jar'
49+
groupId = publishGroupId
50+
artifactId = 'cloudinary-http45'
51+
description = publishDescription
52+
url = githubUrl
53+
licenses {
54+
license {
55+
name = licenseName
56+
url = licenseUrl
57+
}
58+
}
59+
60+
developers {
61+
developer {
62+
id = developerId
63+
name = developerName
64+
email = developerEmail
65+
}
66+
}
67+
scm {
68+
connection = scmConnection
69+
developerConnection = scmDeveloperConnection
70+
url = scmUrl
71+
}
72+
}
73+
74+
pom.withXml {
75+
def pomFile = file("${project.buildDir}/generated-pom.xml")
76+
writeTo(pomFile)
77+
def pomAscFile = signing.sign(pomFile).signatureFiles[0]
78+
artifact(pomAscFile) {
79+
classifier = null
80+
extension = 'pom.asc'
81+
}
82+
}
83+
84+
// create the signed artifacts
85+
project.tasks.signArchives.signatureFiles.each {
86+
artifact(it) {
87+
def matcher = it.file =~ /-(sources|javadoc)\.jar\.asc$/
88+
if (matcher.find()) {
89+
classifier = matcher.group(1)
90+
} else {
91+
classifier = null
92+
}
93+
extension = 'jar.asc'
94+
}
95+
}
96+
}
97+
}
98+
99+
nexusPublishing {
100+
repositories {
101+
sonatype {
102+
username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : ""
103+
password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : ""
104+
}
105+
}
106+
}
107+
108+
model {
109+
tasks.generatePomFileForMavenJavaPublication {
110+
destination = file("$buildDir/generated-pom.xml")
111+
}
112+
tasks.publishMavenJavaPublicationToMavenLocal {
113+
dependsOn project.tasks.signArchives
114+
}
115+
tasks.publishMavenJavaPublicationToSonatypeRepository {
116+
dependsOn project.tasks.signArchives
117+
}
118+
}
119+
}
120+
}
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
package com.cloudinary.http45;
2+
3+
import com.cloudinary.Api;
4+
import com.cloudinary.Api.HttpMethod;
5+
import com.cloudinary.Cloudinary;
6+
import com.cloudinary.api.ApiResponse;
7+
import com.cloudinary.api.exceptions.GeneralError;
8+
import com.cloudinary.http45.api.Response;
9+
import com.cloudinary.utils.Base64Coder;
10+
import com.cloudinary.utils.ObjectUtils;
11+
import com.cloudinary.utils.StringUtils;
12+
import org.apache.http.Consts;
13+
import org.apache.http.HttpHost;
14+
import org.apache.http.NameValuePair;
15+
import org.apache.http.client.config.RequestConfig;
16+
import org.apache.http.client.entity.UrlEncodedFormEntity;
17+
import org.apache.http.client.methods.*;
18+
import org.apache.http.client.utils.URIBuilder;
19+
import org.apache.http.conn.HttpClientConnectionManager;
20+
import org.apache.http.entity.ContentType;
21+
import org.apache.http.entity.StringEntity;
22+
import org.apache.http.impl.client.CloseableHttpClient;
23+
import org.apache.http.impl.client.HttpClientBuilder;
24+
import org.apache.http.impl.client.HttpClients;
25+
import org.cloudinary.json.JSONException;
26+
import org.cloudinary.json.JSONObject;
27+
28+
import java.io.InputStream;
29+
import java.io.UnsupportedEncodingException;
30+
import java.lang.reflect.Constructor;
31+
import java.net.URI;
32+
import java.net.URISyntaxException;
33+
import java.util.Arrays;
34+
import java.util.HashMap;
35+
import java.util.List;
36+
import java.util.Map;
37+
38+
import static com.cloudinary.http45.ApiUtils.prepareParams;
39+
import static com.cloudinary.http45.ApiUtils.setTimeouts;
40+
41+
public class ApiStrategy extends com.cloudinary.strategies.AbstractApiStrategy {
42+
43+
private CloseableHttpClient client = null;
44+
45+
@Override
46+
public void init(Api api) {
47+
super.init(api);
48+
49+
HttpClientBuilder clientBuilder = HttpClients.custom();
50+
clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " ApacheHTTPComponents/4.5");
51+
52+
// If the configuration specifies a proxy then apply it to the client
53+
if (api.cloudinary.config.proxyHost != null && api.cloudinary.config.proxyPort != 0) {
54+
HttpHost proxy = new HttpHost(api.cloudinary.config.proxyHost, api.cloudinary.config.proxyPort);
55+
clientBuilder.setProxy(proxy);
56+
}
57+
58+
HttpClientConnectionManager connectionManager = (HttpClientConnectionManager) api.cloudinary.config.properties.get("connectionManager");
59+
if (connectionManager != null) {
60+
clientBuilder.setConnectionManager(connectionManager);
61+
}
62+
63+
int timeout = this.api.cloudinary.config.timeout;
64+
if (timeout > 0) {
65+
RequestConfig config = RequestConfig.custom()
66+
.setSocketTimeout(timeout * 1000)
67+
.setConnectTimeout(timeout * 1000)
68+
.build();
69+
clientBuilder.setDefaultRequestConfig(config);
70+
}
71+
72+
this.client = clientBuilder.build();
73+
}
74+
75+
@SuppressWarnings({"rawtypes", "unchecked"})
76+
public ApiResponse callApi(HttpMethod method, Iterable<String> uri, Map<String, ?> params, Map options) throws Exception {
77+
if (options == null)
78+
options = ObjectUtils.emptyMap();
79+
80+
String prefix = ObjectUtils.asString(options.get("upload_prefix"), ObjectUtils.asString(this.api.cloudinary.config.uploadPrefix, "https://api.cloudinary.com"));
81+
String cloudName = ObjectUtils.asString(options.get("cloud_name"), this.api.cloudinary.config.cloudName);
82+
if (cloudName == null) throw new IllegalArgumentException("Must supply cloud_name");
83+
String apiKey = ObjectUtils.asString(options.get("api_key"), this.api.cloudinary.config.apiKey);
84+
String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.api.cloudinary.config.apiSecret);
85+
String oauthToken = ObjectUtils.asString(options.get("oauth_token"), this.api.cloudinary.config.oauthToken);
86+
87+
validateAuthorization(apiKey, apiSecret, oauthToken);
88+
89+
90+
String apiUrl = createApiUrl(uri, prefix, cloudName);
91+
HttpUriRequest request = prepareRequest(method, apiUrl, params, options);
92+
93+
request.setHeader("Authorization", getAuthorizationHeaderValue(apiKey, apiSecret, oauthToken));
94+
95+
return getApiResponse(request);
96+
}
97+
98+
private ApiResponse getApiResponse(HttpUriRequest request) throws Exception {
99+
String responseData = null;
100+
int code = 0;
101+
CloseableHttpResponse response = client.execute(request);
102+
try {
103+
code = response.getStatusLine().getStatusCode();
104+
InputStream responseStream = response.getEntity().getContent();
105+
responseData = StringUtils.read(responseStream);
106+
} finally {
107+
response.close();
108+
}
109+
110+
Class<? extends Exception> exceptionClass = Api.CLOUDINARY_API_ERROR_CLASSES.get(code);
111+
if (code != 200 && exceptionClass == null) {
112+
throw new GeneralError("Server returned unexpected status code - " + code + " - " + responseData);
113+
}
114+
Map result;
115+
116+
try {
117+
JSONObject responseJSON = new JSONObject(responseData);
118+
result = ObjectUtils.toMap(responseJSON);
119+
} catch (JSONException e) {
120+
throw new RuntimeException("Invalid JSON response from server " + e.getMessage());
121+
}
122+
123+
if (code == 200) {
124+
return new Response(response, result);
125+
} else {
126+
String message = (String) ((Map) result.get("error")).get("message");
127+
Constructor<? extends Exception> exceptionConstructor = exceptionClass.getConstructor(String.class);
128+
throw exceptionConstructor.newInstance(message);
129+
}
130+
}
131+
132+
@Override
133+
public ApiResponse callAccountApi(HttpMethod method, Iterable<String> uri, Map<String, ?> params, Map options) throws Exception {
134+
if (options == null)
135+
options = ObjectUtils.emptyMap();
136+
137+
String prefix = ObjectUtils.asString(options.get("upload_prefix"), "https://api.cloudinary.com");
138+
String apiKey = ObjectUtils.asString(options.get("provisioning_api_key"));
139+
if (apiKey == null) throw new IllegalArgumentException("Must supply provisioning_api_key");
140+
String apiSecret = ObjectUtils.asString(options.get("provisioning_api_secret"));
141+
if (apiSecret == null) throw new IllegalArgumentException("Must supply provisioning_api_secret");
142+
143+
String apiUrl = StringUtils.join(Arrays.asList(prefix, "v1_1"), "/");
144+
for (String component : uri) {
145+
apiUrl = apiUrl + "/" + component;
146+
}
147+
148+
HttpUriRequest request = prepareRequest(method, apiUrl, params, options);
149+
150+
request.setHeader("Authorization", getAuthorizationHeaderValue(apiKey, apiSecret, null));
151+
152+
return getApiResponse(request);
153+
}
154+
155+
/**
156+
* Prepare a request with the URL and parameters based on the HTTP method used
157+
*
158+
* @param method the HTTP method: GET, PUT, POST, DELETE
159+
* @param apiUrl the cloudinary API URI
160+
* @param params the parameters to pass to the server
161+
* @return an HTTP request
162+
* @throws URISyntaxException
163+
* @throws UnsupportedEncodingException
164+
*/
165+
private HttpUriRequest prepareRequest(HttpMethod method, String apiUrl, Map<String, ?> params, Map options) throws URISyntaxException, UnsupportedEncodingException {
166+
URI apiUri;
167+
HttpRequestBase request;
168+
169+
String contentType = ObjectUtils.asString(options.get("content_type"), "urlencoded");
170+
URIBuilder apiUrlBuilder = new URIBuilder(apiUrl);
171+
List<NameValuePair> urlEncodedParams = prepareParams(params);
172+
173+
if (method == HttpMethod.GET) {
174+
apiUrlBuilder.setParameters(prepareParams(params));
175+
apiUri = apiUrlBuilder.build();
176+
request = new HttpGet(apiUri);
177+
} else {
178+
Map<String,Object> paramsCopy = new HashMap<String, Object>((Map<String,Object>) params);
179+
apiUri = apiUrlBuilder.build();
180+
switch (method) {
181+
case PUT:
182+
request = new HttpPut(apiUri);
183+
break;
184+
case DELETE: //uses HttpPost instead of HttpDelete
185+
paramsCopy.put("_method", "delete");
186+
//continue with POST
187+
case POST:
188+
request = new HttpPost(apiUri);
189+
break;
190+
default:
191+
throw new IllegalArgumentException("Unknown HTTP method");
192+
}
193+
if (contentType.equals("json")) {
194+
JSONObject asJSON = ObjectUtils.toJSON(paramsCopy);
195+
StringEntity requestEntity = new StringEntity(asJSON.toString(), ContentType.APPLICATION_JSON);
196+
((HttpEntityEnclosingRequestBase) request).setEntity(requestEntity);
197+
} else {
198+
((HttpEntityEnclosingRequestBase) request).setEntity(new UrlEncodedFormEntity(prepareParams(paramsCopy), Consts.UTF_8));
199+
}
200+
}
201+
202+
setTimeouts(request, options);
203+
return request;
204+
}
205+
}

0 commit comments

Comments
 (0)