/* * Copyright 2013 Jakob Vad Nielsen * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.jakobnielsen.imagga.client; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.SocketTimeoutException; import java.net.URL; import java.net.URLEncoder; import java.security.MessageDigest; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import static net.jakobnielsen.imagga.ListTools.implode; public class APIClient { protected static final String TRUE_VALUE = "1"; protected static final String FALSE_VALUE = "0"; private final APIClientConfig apiConfig; private final String apiService; protected APIClient(APIClientConfig apiConfig, String apiService) { this.apiConfig = apiConfig; this.apiService = apiService; } String getApiUrl() { return "http://" + this.apiConfig.getEndpoint(); } String getApiKey() { return this.apiConfig.getKey(); } String getApiSecret() { return this.apiConfig.getSecret(); } String getApiService() { return apiService; } public APIClientConfig getApiConfig() { return apiConfig; } public String getServerAddr() { return getApiUrl() + "/" + getApiService() + ".php"; } protected String callMethod(Method method) throws APIClientException { return callMethod(method, "application/x-www-form-urlencoded"); } protected String callMethod(Method method, String contentType) throws APIClientException { try { String postString = createPostString(method); URL url = new URL(getServerAddr()); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setDoInput(true); connection.setInstanceFollowRedirects(false); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", contentType); connection.setRequestProperty("Charset", ApiConstants.CHARSET); connection.setRequestProperty("Content-Length", Integer.toString(postString.getBytes().length)); connection.setRequestProperty("User-Agent", "Crop & Slice API JAVA Client"); connection.setUseCaches(false); connection.setConnectTimeout(apiConfig.getConnectionTimeout()); connection.setReadTimeout(apiConfig.getReadTimeout()); DataOutputStream wr = new DataOutputStream(connection.getOutputStream()); wr.writeBytes(postString); wr.flush(); wr.close(); InputStream in = null; InputStreamReader is = null; BufferedReader br = null; try { if (connection.getResponseCode() == 200) { in = connection.getInputStream(); is = new InputStreamReader(in); StringBuilder sb = new StringBuilder(); br = new BufferedReader(is); String read = br.readLine(); while (read != null) { sb.append(read); read = br.readLine(); } return sb.toString(); } else { throw new IOException( "Error " + connection.getResponseCode() + " : " + connection.getResponseMessage()); } } finally { connection.disconnect(); if (in != null) { try { in.close(); } catch (IOException e) { /* Ignored since there is no recovery needed */ } } if (is != null) { try { is.close(); } catch (IOException e) { /* Ignored since there is no recovery needed */ } } if (br != null) { try { br.close(); } catch (IOException e) { /* Ignored since there is no recovery needed */ } } } } catch (SocketTimeoutException e) { throw new APIClientException("Connection to " + getServerAddr() + " timed out.", e); } catch (IOException e) { throw new APIClientException(e.getMessage(), e); } } /* http://imagga.com/api/docs/how-to-generate-api-call-signature.html */ String generateSig(Map<String, List<String>> params) { StringBuffer str = new StringBuffer(); List<String> sortedKeys = new ArrayList<String>(params.keySet()); Collections.sort(sortedKeys); for (String s : sortedKeys) { str.append(s).append("=").append(implode(params.get(s))); } str.append(getApiSecret()); return md5(str); } String createPostString(Method method) throws UnsupportedEncodingException { List<String> postParams = new ArrayList<String>(); method.addParam(ApiConstants.API_KEY_PARAM, getApiKey()); if (!method.hasParam(ApiConstants.VERSION_PARAM)) { method.addParam(ApiConstants.VERSION_PARAM, "1.0"); } for (String name : method.getParams().keySet()) { List<String> paramList = method.getParam(name); postParams.add(name + "=" + URLEncoder.encode(implode(paramList), ApiConstants.CHARSET)); } postParams.add(ApiConstants.SIGNATURE_PARAM + "=" + generateSig(method.getParams())); return implode(postParams, "&"); } private String md5(StringBuffer str) { StringWriter result = new StringWriter(); try { MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update(str.toString().getBytes(ApiConstants.CHARSET)); byte[] hashValue = md5.digest(); for (byte aHashValue : hashValue) { String word = Integer.toString(aHashValue & 0xff, 16).toUpperCase(); if (word.length() == 1) { result.write("0"); } result.write(word); } } catch (UnsupportedEncodingException u) { return null; } catch (java.security.NoSuchAlgorithmException n) { return null; } return result.toString().toLowerCase(); } }