Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ env:
- MODULE=http42
- MODULE=http43
- MODULE=http44
- MODULE=http45

branches:
except:
Expand Down
6 changes: 4 additions & 2 deletions cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ public class Cloudinary {
"com.cloudinary.android.UploaderStrategy",
"com.cloudinary.http42.UploaderStrategy",
"com.cloudinary.http43.UploaderStrategy",
"com.cloudinary.http44.UploaderStrategy"));
"com.cloudinary.http44.UploaderStrategy",
"com.cloudinary.http45.UploaderStrategy"));
public static List<String> API_STRATEGIES = new ArrayList<String>(Arrays.asList(
"com.cloudinary.android.ApiStrategy",
"com.cloudinary.http42.ApiStrategy",
"com.cloudinary.http43.ApiStrategy",
"com.cloudinary.http44.ApiStrategy"));
"com.cloudinary.http44.ApiStrategy",
"com.cloudinary.http45.ApiStrategy"));

public final static String CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net";
public final static String OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net";
Expand Down
120 changes: 120 additions & 0 deletions cloudinary-http45/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
plugins {
id 'java-library'
id 'signing'
id 'maven-publish'
id 'io.codearte.nexus-staging' version '0.21.1'
id "de.marcphilipp.nexus-publish" version "0.4.0"
}

apply from: "../java_shared.gradle"

task ciTest( type: Test ) {
useJUnit {
excludeCategories 'com.cloudinary.test.TimeoutTest'
}
}

dependencies {
compile project(':cloudinary-core')
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.1'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't want to bump that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can bump in a separate PR, it's not really related to the client and may require changes in other modules as well.

compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.13'
compile group: 'org.apache.httpcomponents', name: 'httpmime', version: '4.5.13'
testCompile project(':cloudinary-test-common')
testCompile group: 'org.hamcrest', name: 'java-hamcrest', version: '2.0.0.0'
testCompile group: 'pl.pragmatists', name: 'JUnitParams', version: '1.0.5'
testCompile group: 'junit', name: 'junit', version: '4.12'
}

if (hasProperty("ossrhPassword")) {

signing {
sign configurations.archives
}

nexusStaging {
packageGroup = group
username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : ""
password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : ""
}

publishing {
publications {
mavenJava(MavenPublication) {
from components.java
artifact sourcesJar
artifact javadocJar
pom {
name = 'Cloudinary Apache HTTP 4.5 Library'
packaging = 'jar'
groupId = publishGroupId
artifactId = 'cloudinary-http45'
description = publishDescription
url = githubUrl
licenses {
license {
name = licenseName
url = licenseUrl
}
}

developers {
developer {
id = developerId
name = developerName
email = developerEmail
}
}
scm {
connection = scmConnection
developerConnection = scmDeveloperConnection
url = scmUrl
}
}

pom.withXml {
def pomFile = file("${project.buildDir}/generated-pom.xml")
writeTo(pomFile)
def pomAscFile = signing.sign(pomFile).signatureFiles[0]
artifact(pomAscFile) {
classifier = null
extension = 'pom.asc'
}
}

// create the signed artifacts
project.tasks.signArchives.signatureFiles.each {
artifact(it) {
def matcher = it.file =~ /-(sources|javadoc)\.jar\.asc$/
if (matcher.find()) {
classifier = matcher.group(1)
} else {
classifier = null
}
extension = 'jar.asc'
}
}
}
}

nexusPublishing {
repositories {
sonatype {
username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : ""
password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : ""
}
}
}

model {
tasks.generatePomFileForMavenJavaPublication {
destination = file("$buildDir/generated-pom.xml")
}
tasks.publishMavenJavaPublicationToMavenLocal {
dependsOn project.tasks.signArchives
}
tasks.publishMavenJavaPublicationToSonatypeRepository {
dependsOn project.tasks.signArchives
}
}
}
}
205 changes: 205 additions & 0 deletions cloudinary-http45/src/main/java/com/cloudinary/http45/ApiStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
package com.cloudinary.http45;

import com.cloudinary.Api;
import com.cloudinary.Api.HttpMethod;
import com.cloudinary.Cloudinary;
import com.cloudinary.api.ApiResponse;
import com.cloudinary.api.exceptions.GeneralError;
import com.cloudinary.http45.api.Response;
import com.cloudinary.utils.Base64Coder;
import com.cloudinary.utils.ObjectUtils;
import com.cloudinary.utils.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.cloudinary.json.JSONException;
import org.cloudinary.json.JSONObject;

import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.cloudinary.http45.ApiUtils.prepareParams;
import static com.cloudinary.http45.ApiUtils.setTimeouts;

public class ApiStrategy extends com.cloudinary.strategies.AbstractApiStrategy {

private CloseableHttpClient client = null;

@Override
public void init(Api api) {
super.init(api);

HttpClientBuilder clientBuilder = HttpClients.custom();
clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " ApacheHTTPComponents/4.5");

// If the configuration specifies a proxy then apply it to the client
if (api.cloudinary.config.proxyHost != null && api.cloudinary.config.proxyPort != 0) {
HttpHost proxy = new HttpHost(api.cloudinary.config.proxyHost, api.cloudinary.config.proxyPort);
clientBuilder.setProxy(proxy);
}

HttpClientConnectionManager connectionManager = (HttpClientConnectionManager) api.cloudinary.config.properties.get("connectionManager");
if (connectionManager != null) {
clientBuilder.setConnectionManager(connectionManager);
}

int timeout = this.api.cloudinary.config.timeout;
if (timeout > 0) {
RequestConfig config = RequestConfig.custom()
.setSocketTimeout(timeout * 1000)
.setConnectTimeout(timeout * 1000)
.build();
clientBuilder.setDefaultRequestConfig(config);
}

this.client = clientBuilder.build();
}

@SuppressWarnings({"rawtypes", "unchecked"})
public ApiResponse callApi(HttpMethod method, Iterable<String> uri, Map<String, ?> params, Map options) throws Exception {
if (options == null)
options = ObjectUtils.emptyMap();

String prefix = ObjectUtils.asString(options.get("upload_prefix"), ObjectUtils.asString(this.api.cloudinary.config.uploadPrefix, "https://api.cloudinary.com"));
String cloudName = ObjectUtils.asString(options.get("cloud_name"), this.api.cloudinary.config.cloudName);
if (cloudName == null) throw new IllegalArgumentException("Must supply cloud_name");
String apiKey = ObjectUtils.asString(options.get("api_key"), this.api.cloudinary.config.apiKey);
String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.api.cloudinary.config.apiSecret);
String oauthToken = ObjectUtils.asString(options.get("oauth_token"), this.api.cloudinary.config.oauthToken);

validateAuthorization(apiKey, apiSecret, oauthToken);


String apiUrl = createApiUrl(uri, prefix, cloudName);
HttpUriRequest request = prepareRequest(method, apiUrl, params, options);

request.setHeader("Authorization", getAuthorizationHeaderValue(apiKey, apiSecret, oauthToken));

return getApiResponse(request);
}

private ApiResponse getApiResponse(HttpUriRequest request) throws Exception {
String responseData = null;
int code = 0;
CloseableHttpResponse response = client.execute(request);
try {
code = response.getStatusLine().getStatusCode();
InputStream responseStream = response.getEntity().getContent();
responseData = StringUtils.read(responseStream);
} finally {
response.close();
}

Class<? extends Exception> exceptionClass = Api.CLOUDINARY_API_ERROR_CLASSES.get(code);
if (code != 200 && exceptionClass == null) {
throw new GeneralError("Server returned unexpected status code - " + code + " - " + responseData);
}
Map result;

try {
JSONObject responseJSON = new JSONObject(responseData);
result = ObjectUtils.toMap(responseJSON);
} catch (JSONException e) {
throw new RuntimeException("Invalid JSON response from server " + e.getMessage());
}

if (code == 200) {
return new Response(response, result);
} else {
String message = (String) ((Map) result.get("error")).get("message");
Constructor<? extends Exception> exceptionConstructor = exceptionClass.getConstructor(String.class);
throw exceptionConstructor.newInstance(message);
}
}

@Override
public ApiResponse callAccountApi(HttpMethod method, Iterable<String> uri, Map<String, ?> params, Map options) throws Exception {
if (options == null)
options = ObjectUtils.emptyMap();

String prefix = ObjectUtils.asString(options.get("upload_prefix"), "https://api.cloudinary.com");
String apiKey = ObjectUtils.asString(options.get("provisioning_api_key"));
if (apiKey == null) throw new IllegalArgumentException("Must supply provisioning_api_key");
String apiSecret = ObjectUtils.asString(options.get("provisioning_api_secret"));
if (apiSecret == null) throw new IllegalArgumentException("Must supply provisioning_api_secret");

String apiUrl = StringUtils.join(Arrays.asList(prefix, "v1_1"), "/");
for (String component : uri) {
apiUrl = apiUrl + "/" + component;
}

HttpUriRequest request = prepareRequest(method, apiUrl, params, options);

request.setHeader("Authorization", getAuthorizationHeaderValue(apiKey, apiSecret, null));

return getApiResponse(request);
}

/**
* Prepare a request with the URL and parameters based on the HTTP method used
*
* @param method the HTTP method: GET, PUT, POST, DELETE
* @param apiUrl the cloudinary API URI
* @param params the parameters to pass to the server
* @return an HTTP request
* @throws URISyntaxException
* @throws UnsupportedEncodingException
*/
private HttpUriRequest prepareRequest(HttpMethod method, String apiUrl, Map<String, ?> params, Map options) throws URISyntaxException, UnsupportedEncodingException {
URI apiUri;
HttpRequestBase request;

String contentType = ObjectUtils.asString(options.get("content_type"), "urlencoded");
URIBuilder apiUrlBuilder = new URIBuilder(apiUrl);
List<NameValuePair> urlEncodedParams = prepareParams(params);

if (method == HttpMethod.GET) {
apiUrlBuilder.setParameters(prepareParams(params));
apiUri = apiUrlBuilder.build();
request = new HttpGet(apiUri);
} else {
Map<String,Object> paramsCopy = new HashMap<String, Object>((Map<String,Object>) params);
apiUri = apiUrlBuilder.build();
switch (method) {
case PUT:
request = new HttpPut(apiUri);
break;
case DELETE: //uses HttpPost instead of HttpDelete
paramsCopy.put("_method", "delete");
//continue with POST
case POST:
request = new HttpPost(apiUri);
break;
default:
throw new IllegalArgumentException("Unknown HTTP method");
}
if (contentType.equals("json")) {
JSONObject asJSON = ObjectUtils.toJSON(paramsCopy);
StringEntity requestEntity = new StringEntity(asJSON.toString(), ContentType.APPLICATION_JSON);
((HttpEntityEnclosingRequestBase) request).setEntity(requestEntity);
} else {
((HttpEntityEnclosingRequestBase) request).setEntity(new UrlEncodedFormEntity(prepareParams(paramsCopy), Consts.UTF_8));
}
}

setTimeouts(request, options);
return request;
}
}
Loading