Java tutorial
/******************************************************************************* * Copyright (c) 2011 GigaSpaces Technologies Ltd. All rights reserved * * 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 org.cloudifysource.restclient; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.net.URL; import java.nio.charset.Charset; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.beanutils.BeanUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpException; import org.apache.http.HttpHeaders; import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; import org.apache.http.HttpResponse; import org.apache.http.HttpVersion; import org.apache.http.StatusLine; import org.apache.http.auth.AuthScope; import org.apache.http.auth.Credentials; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.entity.StringEntity; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.FileBody; import org.apache.http.entity.mime.content.StringBody; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpParams; import org.apache.http.params.HttpProtocolParams; import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HttpContext; import org.cloudifysource.dsl.internal.CloudifyConstants; import org.cloudifysource.dsl.internal.CloudifyErrorMessages; import org.cloudifysource.dsl.rest.response.ControllerDetails; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.type.TypeFactory; import org.codehaus.jackson.type.JavaType; /** * @author rafi * @since 2.0.0 */ public class GSRestClient { /** * constants. */ public static final String REASON_CODE_COMM_ERR = CloudifyErrorMessages.COMMUNICATION_ERROR.getName(); private static final String STATUS_KEY = "status"; private static final String ERROR = "error"; // org.springframework.http.HttpStatus.OK.value(); private static final Logger logger = Logger.getLogger(GSRestClient.class.getName()); private static final String ERROR_ARGS = "error_args"; private static final String VERBOSE = "verbose"; private static final ObjectMapper PROJECT_MAPPER = new ObjectMapper(); private static final String RESPONSE_KEY = "response"; private static final String ADMIN_REFLECTION_URL = "/admin/"; private static final String FORWARD_SLASH = "/"; private static final String HTTPS = "https"; private static final String MSG_RESPONSE_CODE = " response code "; private static final String MSG_RESPONSE_REASON_PHRASE = "reason phrase"; private static final String MSG_RESPONSE_ENTITY_NULL = " response entity is null"; private static final String MSG_HTTP_GET_RESPONSE = " http get response: "; private static final String MSG_REST_API_ERR = " Rest api error"; private static final String MIME_TYPE_APP_JSON = "application/json"; // TODO change when legit certificate is available private final DefaultHttpClient httpClient; private final URL url; private final String urlStr; /** * Ctor. * * @param username * Username for the HTTP client, optional. * @param password * Password for the HTTP client, optional. * @param url * URL to the rest service. * @param version * cloudify api version of the client * @throws RestException * Reporting failure to create a SSL HTTP client. */ public GSRestClient(final String username, final String password, final URL url, final String version) throws RestException { this.url = url; this.urlStr = createUrlStr(); if (isSSL()) { httpClient = getSSLHttpClient(); } else { httpClient = new DefaultHttpClient(); } httpClient.addRequestInterceptor(new HttpRequestInterceptor() { @Override public void process(HttpRequest request, HttpContext context) throws HttpException, IOException { request.addHeader(CloudifyConstants.REST_API_VERSION_HEADER, version); } }); setCredentials(username, password); } /** * Ctor. * * @param credentials * credentials for the HTTP client. * @param url * URL to the rest service. * @param version * cloudify api version of the client * @throws RestException * Reporting failure to create a SSL HTTP client. */ public GSRestClient(Credentials credentials, final URL url, final String version) throws RestException { this.url = url; this.urlStr = createUrlStr(); if (isSSL()) { httpClient = getSSLHttpClient(); } else { httpClient = new DefaultHttpClient(); } httpClient.addRequestInterceptor(new HttpRequestInterceptor() { @Override public void process(HttpRequest request, HttpContext context) throws HttpException, IOException { request.addHeader(CloudifyConstants.REST_API_VERSION_HEADER, version); } }); setCredentials(credentials); } /** * Sets username and password for the HTTP client * * @param username * Username for the HTTP client. * @param password * Password for the HTTP client. */ public void setCredentials(final String username, final String password) { // TODO use userdetails instead of user/pass if (StringUtils.notEmpty(username) && StringUtils.notEmpty(password)) { httpClient.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY), new UsernamePasswordCredentials(username, password)); } } /** * Sets the credentials for the HTTP client. * * @param credentials * The credentials for the HTTP client */ public void setCredentials(final Credentials credentials) { httpClient.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY), credentials); } /** * Creates the basic rest service URL. Relative URLs will be appended to this URL. * * @return the basic rest service URL */ private String createUrlStr() { String urlStr = url.toExternalForm(); if (!urlStr.endsWith(FORWARD_SLASH)) { urlStr += FORWARD_SLASH; } return urlStr; } /** * Checks if the Rest URL uses SSL (https instead of http). * * @return boolean indicating if ssl is used */ private boolean isSSL() { return HTTPS.equals(url.getProtocol()); } /** * Performs a REST GET operation on the given (relative) URL, using the Admin API. * * @param relativeUrl * the Relative URL to the requested object. The rest server IP and port are not required. * <p/> * example: "processingUnits/Names/default.cassandra" will get the object named "dafault.cassandra" from * the list of all processing units. * @return An object, the response received from the Admin API. * @throws RestException * Reporting errors of all types (IO, HTTP, rest etc.) */ public final Map<String, Object> getAdminData(final String relativeUrl) throws RestException { final String url = getFullUrl("admin/" + relativeUrl); final HttpGet httpMethod = new HttpGet(url); return readHttpAdminMethod(httpMethod); } /** * Performs a REST GET operation on the given (relative) URL. * * @param relativeUrl * the Relative URL to the requested object. The rest server IP and port are not required. * <p/> * example: "/service/applications/travel/services/cassandra/ USMEventsLogs/" will get event logs from * the cassandra service of the travel application. * @return An object, the response received from the rest service * @throws ErrorStatusException * Reporting errors of all types (IO, HTTP, rest etc.) */ public final Object get(final String relativeUrl) throws ErrorStatusException { return get(relativeUrl, "response"); } /** * Calls HttpGet on the given relative url. * @param relativeUrl . * @param reponseJsonKey . * @return The output of the Http Get call * @throws ErrorStatusException . */ public final Object get(final String relativeUrl, final String reponseJsonKey) throws ErrorStatusException { final String url = getFullUrl(relativeUrl); final HttpGet httpMethod = new HttpGet(url); return executeHttpMethod(httpMethod, reponseJsonKey); } /** * Performs a REST GET operation on the given (relative) URL, using the Admin API. * * @param relativeUrl * the Relative URL to the requested object. The rest server IP and port are not required. * <p/> * example: "applications/Names/travel/ProcessingUnits/Names/travel.tomcat * /Instances/0/GridServiceContainer/Uid" will get the UID of the first instance of the tomcat service in * the "travel" application. * @return An object, the response received from the Admin API. * @throws RestException * Reporting errors of all types (IO, HTTP, rest etc.) */ public final Map<String, Object> getAdmin(final String relativeUrl) throws RestException { final String url = getFullUrl(ADMIN_REFLECTION_URL + relativeUrl); final HttpGet httpMethod = new HttpGet(url); InputStream instream = null; try { final HttpResponse response = httpClient.execute(httpMethod); if (response.getStatusLine().getStatusCode() != CloudifyConstants.HTTP_STATUS_CODE_OK) { logger.log(Level.FINE, httpMethod.getURI() + MSG_RESPONSE_CODE + response.getStatusLine().getStatusCode()); throw new RestException(response.getStatusLine().toString()); } final HttpEntity entity = response.getEntity(); if (entity == null) { final ErrorStatusException e = new ErrorStatusException(REASON_CODE_COMM_ERR, httpMethod.getURI(), MSG_RESPONSE_ENTITY_NULL); logger.log(Level.FINE, httpMethod.getURI() + MSG_RESPONSE_ENTITY_NULL, e); throw e; } instream = entity.getContent(); final String responseBody = StringUtils.getStringFromStream(instream); logger.finer(httpMethod.getURI() + MSG_HTTP_GET_RESPONSE + responseBody); final Map<String, Object> responseMap = GSRestClient.jsonToMap(responseBody); return responseMap; } catch (final ClientProtocolException e) { logger.log(Level.FINE, httpMethod.getURI() + MSG_REST_API_ERR, e); throw new ErrorStatusException(e, REASON_CODE_COMM_ERR, httpMethod.getURI(), MSG_REST_API_ERR); } catch (final IOException e) { logger.log(Level.FINE, httpMethod.getURI() + MSG_REST_API_ERR, e); throw new ErrorStatusException(e, REASON_CODE_COMM_ERR, httpMethod.getURI(), MSG_REST_API_ERR); } finally { if (instream != null) { try { instream.close(); } catch (final IOException e) { } } httpMethod.abort(); } } /** * This method executes the given Http request and analyzes the response. Successful responses are expected to be * formatted as json strings, and are converted to a Map<String, Object> object. In this map these keys can be * expected: "status" (success/error), "error"(reason code), "error_args" and "response". * <p/> * Errors of all types (IO, Http, rest etc.) are reported through an ErrorStatusException. * * @param httpMethod * The http request to perform. * @return An object, the response body received from the rest service * @throws ErrorStatusException * Reporting errors of all types (IO, HTTP, rest etc.) */ private Object executeHttpMethod(final HttpRequestBase httpMethod) throws ErrorStatusException { return executeHttpMethod(httpMethod, "response"); } /** * This method executes the given Http request and analyzes the response. Successful responses are expected to be * formatted as json strings, and are converted to a Map<String, Object> object. In this map these keys can be * expected: "status" (success/error), "error"(reason code), "error_args" and "response". * <p/> * Errors of all types (IO, Http, rest etc.) are reported through an ErrorStatusException. * * @param httpMethod * The http request to perform. * @param responseJsonKey * specify a key for a response attribute to be returned, null means return the entire response * @return An object, the response body received from the rest service * @throws ErrorStatusException * Reporting errors of all types (IO, HTTP, rest etc.) */ private Object executeHttpMethod(final HttpRequestBase httpMethod, final String responseJsonKey) throws ErrorStatusException { String responseBody; try { final HttpResponse response = httpClient.execute(httpMethod); final int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != CloudifyConstants.HTTP_STATUS_CODE_OK) { final String reasonPhrase = response.getStatusLine().getReasonPhrase(); if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, httpMethod.getURI() + MSG_RESPONSE_CODE + statusCode + ", " + MSG_RESPONSE_REASON_PHRASE + ": " + reasonPhrase); } responseBody = getResponseBody(response, httpMethod); if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, httpMethod.getURI() + " response body " + responseBody); } if (statusCode == CloudifyConstants.HTTP_STATUS_NOT_FOUND) { throw new ErrorStatusException("URL_not_found", httpMethod.getURI()); } else if (statusCode == CloudifyConstants.HTTP_STATUS_ACCESS_DENIED) { throw new ErrorStatusException(CloudifyErrorMessages.NO_PERMISSION_ACCESS_DENIED.getName(), httpMethod.getURI()); } else if (statusCode == CloudifyConstants.HTTP_STATUS_UNAUTHORIZED) { throw new ErrorStatusException(CloudifyErrorMessages.UNAUTHORIZED.getName(), reasonPhrase, httpMethod.getURI()); } final Map<String, Object> errorMap = GSRestClient.jsonToMap(responseBody); final String status = (String) errorMap.get(STATUS_KEY); if (ERROR.equals(status)) { final String reason = (String) errorMap.get(ERROR); @SuppressWarnings("unchecked") final List<Object> reasonsArgs = (List<Object>) errorMap.get(ERROR_ARGS); final ErrorStatusException e = new ErrorStatusException(reason, reasonsArgs != null ? reasonsArgs.toArray() : null); if (errorMap.containsKey(VERBOSE)) { e.setVerboseData((String) errorMap.get(VERBOSE)); } logger.log(Level.FINE, reason, e); throw e; } } responseBody = getResponseBody(response, httpMethod); final Map<String, Object> responseMap = GSRestClient.jsonToMap(responseBody); return responseJsonKey != null ? responseMap.get(RESPONSE_KEY) : responseMap; } catch (final IOException e) { logger.log(Level.INFO, httpMethod.getURI() + MSG_REST_API_ERR, e); throw new ErrorStatusException(e, REASON_CODE_COMM_ERR, httpMethod.getURI(), e.getMessage()); } finally { httpMethod.abort(); } } /** * Gets the HTTP response's body as a String. * * @param response * The HttpResponse object to analyze * @param httpMethod * The HTTP request that originated this response * @return the body of the given HttpResponse object, as a string * @throws ErrorStatusException * Reporting a communication failure * @throws IOException * Reporting a failure to read the response's content */ public static String getResponseBody(final HttpResponse response, final HttpRequestBase httpMethod) throws ErrorStatusException, IOException { InputStream instream = null; try { final HttpEntity entity = response.getEntity(); if (entity == null) { final ErrorStatusException e = new ErrorStatusException(REASON_CODE_COMM_ERR, httpMethod.getURI(), MSG_RESPONSE_ENTITY_NULL); logger.log(Level.FINE, MSG_RESPONSE_ENTITY_NULL, e); throw e; } instream = entity.getContent(); return StringUtils.getStringFromStream(instream); } finally { if (instream != null) { try { instream.close(); } catch (final IOException e) { e.printStackTrace(); } } } } /** * Executes the given HTTP request and analyzes the response. Successful responses are expected to be formatted as * json strings, and are converted to a Map<String, Object> object. The map can use these keys: "status" * (success/error), "error"(reason code), "error_args" and "response". * <p/> * Errors of all types (IO, HTTP, rest etc.) are reported through an ErrorStatusException (RestException). * * @param httpMethod * The HTTP request to perform. * @return An object, the response body received from the rest service * @throws RestException * Reporting errors of all types (IO, HTTP, rest etc.) */ private Map<String, Object> readHttpAdminMethod(final HttpRequestBase httpMethod) throws RestException { InputStream instream = null; URI uri = httpMethod.getURI(); try { final HttpResponse response = httpClient.execute(httpMethod); StatusLine statusLine = response.getStatusLine(); int statusCode = statusLine.getStatusCode(); if (statusCode != CloudifyConstants.HTTP_STATUS_CODE_OK) { final String message = uri + " response (code " + statusCode + ") " + statusLine.toString(); if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, message); } throw new RestException(message); } final HttpEntity entity = response.getEntity(); if (entity == null) { final ErrorStatusException e = new ErrorStatusException(REASON_CODE_COMM_ERR, uri, MSG_RESPONSE_ENTITY_NULL); logger.log(Level.FINE, uri + MSG_RESPONSE_ENTITY_NULL, e); throw e; } instream = entity.getContent(); final String responseBody = StringUtils.getStringFromStream(instream); logger.finer(uri + MSG_HTTP_GET_RESPONSE + responseBody); final Map<String, Object> responseMap = GSRestClient.jsonToMap(responseBody); return responseMap; } catch (final ClientProtocolException e) { logger.log(Level.FINE, uri + MSG_REST_API_ERR, e); throw new ErrorStatusException(e, REASON_CODE_COMM_ERR, uri, MSG_REST_API_ERR); } catch (final IOException e) { logger.log(Level.FINE, uri + MSG_REST_API_ERR, e); throw new ErrorStatusException(e, REASON_CODE_COMM_ERR, uri, MSG_REST_API_ERR); } finally { if (instream != null) { try { instream.close(); } catch (final IOException e) { } } httpMethod.abort(); } } /** * Appends the given relative URL to the basic rest-service URL. * * @param relativeUrl * URL to add to the basic URL * @return full URL as as String */ private String getFullUrl(final String relativeUrl) { String safeRelativeURL = relativeUrl; if (safeRelativeURL.startsWith(FORWARD_SLASH)) { safeRelativeURL = safeRelativeURL.substring(1); } return urlStr + safeRelativeURL; } /** * Performs a REST DELETE operation on the given (relative) URL. * * @param relativeUrl * the Relative URL to the object to be deleted. The rest server IP and port are not required. * <p/> * example: service/applications/default/services/cassandra/undeploy will delete (undeploy) the service * named "cassandra" from the list of all services on the default application. * @return Return object. * @throws ErrorStatusException * Reporting errors of all types (IO, HTTP, rest etc.) */ public final Object delete(final String relativeUrl) throws ErrorStatusException { return delete(relativeUrl, null); } @SuppressWarnings("unchecked") public List<ControllerDetails> getManagers() throws ErrorStatusException { Object retval = get("service/controllers"); List<Object> list = (List<Object>) retval; List<ControllerDetails> result = new ArrayList<ControllerDetails>(list.size()); for (Object object : list) { Map<String, Object> map = (Map<String, Object>) object; ControllerDetails details = new ControllerDetails(); try { BeanUtils.populate(details, map); } catch (IllegalAccessException e) { throw new IllegalStateException("Error while reading response from server: " + e.getMessage(), e); } catch (InvocationTargetException e) { throw new IllegalStateException("Error while reading response from server: " + e.getMessage(), e); } result.add(details); } return result; } @SuppressWarnings("unchecked") public List<ControllerDetails> shutdownManagers() throws ErrorStatusException { Object retval = delete("service/controllers", null); List<Object> list = (List<Object>) retval; List<ControllerDetails> result = new ArrayList<ControllerDetails>(list.size()); for (Object object : list) { Map<String, Object> map = (Map<String, Object>) object; ControllerDetails details = new ControllerDetails(); try { BeanUtils.populate(details, map); } catch (IllegalAccessException e) { throw new IllegalStateException("Error while reading response from server: " + e.getMessage(), e); } catch (InvocationTargetException e) { throw new IllegalStateException("Error while reading response from server: " + e.getMessage(), e); } result.add(details); } return result; } /** * Performs a REST DELETE operation on the given (relative) URL. * * @param relativeUrl * the Relative URL to the object to be deleted. The rest server IP and port are not required. * <p/> * example: service/applications/default/services/cassandra/undeploy will delete (undeploy) the service * named "cassandra" from the list of all services on the default application. * @param params * parameters set on the HttpDelete object * @return Return object. * @throws ErrorStatusException * Reporting errors of all types (IO, HTTP, rest etc.) */ public final Object delete(final String relativeUrl, final Map<String, String> params) throws ErrorStatusException { final HttpDelete httpdelete = new HttpDelete(getFullUrl(relativeUrl)); if (params != null) { for (final Map.Entry<String, String> entry : params.entrySet()) { httpdelete.getParams().setParameter(entry.getKey(), entry.getValue()); } } return executeHttpMethod(httpdelete); } /** * This methods executes HTTP post over REST on the given (relative) URL with the given parameters map. * * @param relativeUrl * The URL to post to. * @return The response object from the REST server * @throws RestException * Reporting failure to post the file. */ public final Object post(final String relativeUrl) throws RestException { return post(relativeUrl, null); } /** * This methods executes HTTP post over REST on the given (relative) URL with the given parameters map. * * @param relativeUrl * The URL to post to. * @param params * parameters as a map of names and values. * @return The response object from the REST server * @throws RestException * Reporting failure to post the file. */ public final Object post(final String relativeUrl, final Map<String, String> params) throws RestException { final HttpPost httppost = new HttpPost(getFullUrl(relativeUrl)); if (params != null) { HttpEntity entity; try { final String json = GSRestClient.mapToJson(params); entity = new StringEntity(json, MIME_TYPE_APP_JSON, "UTF-8"); httppost.setEntity(entity); httppost.setHeader(HttpHeaders.CONTENT_TYPE, MIME_TYPE_APP_JSON); } catch (final IOException e) { throw new RestException(e); } } return executeHttpMethod(httppost); } /** * This methods executes HTTP post over REST on the given (relative) URL with the given file. * * @param relativeUrl * The URL to post to. * @param file * The file to send (example: <SOME PATH>/tomcat.zip). * @return The response object from the REST server * @throws RestException * Reporting failure to post the file. */ public final Object postFile(final String relativeUrl, final File file) throws RestException { return postFile(relativeUrl, file, null/* props */, null/* params */); } /** * This methods executes HTTP post over REST on the given (relative) URL with the given file. * * @param relativeUrl * The URL to post to. * @param file * The file to send (example: <SOME PATH>/tomcat.zip). * @return The response object from the REST server * @throws RestException * Reporting failure to post the file. */ public final Object postFile(final String relativeUrl, final File file, final Properties props, final Map<String, String> params, final File cloudOverrides) throws RestException { Map<String, File> filesToPost = new HashMap<String, File>(); filesToPost.put("file", file); filesToPost.put("cloudOverridesFile", cloudOverrides); return postFiles(relativeUrl, props, params, filesToPost); } public final Object postFiles(final String relativeUrl, Map<String, File> files) throws RestException { return postFiles(relativeUrl, null/* props */, null/* params */, files); } /** * This methods executes HTTP post over REST on the given (relative) URL with the given file and properties (also * sent as a separate file). * * @param relativeUrl * The URL to post to. * @param additionalFiles * The files to send (example: <SOME PATH>/tomcat.zip). * @param props * The properties of this POST action (example: com.gs.service.type=WEB_SERVER) * @param params * as a map of names and values. * @return The response object from the REST server * @throws RestException * Reporting failure to post the file. */ public final Object postFiles(final String relativeUrl, final Properties props, final Map<String, String> params, final Map<String, File> additionalFiles) throws RestException { final MultipartEntity reqEntity = new MultipartEntity(); // It should be possible to dump the properties into a String entity, // but I can't get it to work. So using a temp file instead. // Idiotic, but works. // dump map into file File tempFile; try { tempFile = writeMapToFile(props); } catch (final IOException e) { throw new RestException(e); } final FileBody propsFile = new FileBody(tempFile); reqEntity.addPart("props", propsFile); if (params != null) { try { for (Map.Entry<String, String> param : params.entrySet()) { reqEntity.addPart(param.getKey(), new StringBody(param.getValue(), Charset.forName("UTF-8"))); } } catch (final IOException e) { throw new RestException(e); } } // add the rest of the files for (Entry<String, File> entry : additionalFiles.entrySet()) { final File file = entry.getValue(); if (file != null) { final FileBody bin = new FileBody(file); reqEntity.addPart(entry.getKey(), bin); } } final HttpPost httppost = new HttpPost(getFullUrl(relativeUrl)); httppost.setEntity(reqEntity); return executeHttpMethod(httppost); } /** * This methods executes HTTP post over REST on the given (relative) URL with the given file and properties (also * sent as a separate file). * * @param relativeUrl * The URL to post to. * @param file * The file to send (example: <SOME PATH>/tomcat.zip). * @param props * An additional string parameter passed on this post. * @param params * parameters as a map of names and values. * @return The response object from the REST server * @throws RestException * Reporting failure to post the file. */ public final Object postFile(final String relativeUrl, final File file, final Properties props, final Map<String, String> params) throws RestException { Map<String, File> additionalFiles = new HashMap<String, File>(); additionalFiles.put("file", file); return postFiles(relativeUrl, props, params, additionalFiles); } /** * Writes the given properties to a temporary text files and returns it. The text file will be deleted * automatically. * * @param props * Properties to write to a temporary text (.tmp) file * @return File object - the properties file created. * @throws IOException * Reporting failure to create the text file. */ protected final File writeMapToFile(final Properties props) throws IOException { // then dump properties into file File tempFile = null; FileWriter writer = null; try { tempFile = File.createTempFile("uploadTemp", ".tmp"); tempFile.deleteOnExit(); writer = new FileWriter(tempFile, true); if (props != null) { props.store(writer, ""); } } finally { if (writer != null) { try { writer.close(); } catch (final IOException e) { // ignore } } } return tempFile; } /** * Returns a HTTP client configured to use SSL. * * @return HTTP client configured to use SSL * @throws RestException * Reporting different failures while creating the HTTP client */ public final DefaultHttpClient getSSLHttpClient() throws RestException { try { final KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); // TODO : support self-signed certs if configured by user upon // "connect" trustStore.load(null, null); final SSLSocketFactory sf = new RestSSLSocketFactory(trustStore); sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); final HttpParams params = new BasicHttpParams(); HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); HttpProtocolParams.setContentCharset(params, HTTP.UTF_8); final SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme(HTTPS, sf, url.getPort())); final ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry); return new DefaultHttpClient(ccm, params); } catch (final KeyStoreException e) { throw new RestException(e); } catch (final NoSuchAlgorithmException e) { throw new RestException(e); } catch (final CertificateException e) { throw new RestException(e); } catch (final IOException e) { throw new RestException(e); } catch (final KeyManagementException e) { throw new RestException(e); } catch (final UnrecoverableKeyException e) { throw new RestException(e); } } /** * Converts a json String to a Map<String, Object>. * * @param response * a json-format String to convert to a map * @return a Map<String, Object> based on the given String * @throws ErrorStatusException */ public static Map<String, Object> jsonToMap(final String response) throws ErrorStatusException { try { final JavaType javaType = TypeFactory.type(Map.class); return PROJECT_MAPPER.readValue(response, javaType); } catch (final IOException e) { throw new ErrorStatusException(e, CloudifyErrorMessages.JSON_PARSE_ERROR.getName(), response); } } /** * Converts a Map<String, ?> to a json String. * * @param map * a map to convert to String * @return a json-format String based on the given map * @throws IOException * Reporting failure to read the map or convert it */ public static String mapToJson(final Map<String, ?> map) throws IOException { return PROJECT_MAPPER.writeValueAsString(map); } /** * Convert a Map<String, Object> to an InvocationResult object. * * @param map * a map to convert to an InvocationResult object * @return an InvocationResult object based on the given map * @throws IOException * Reporting failure to read the map or convert it */ public static InvocationResult mapToInvocationResult(final Map<String, Object> map) throws IOException { final String json = GSRestClient.mapToJson(map); final JavaType javaType = TypeFactory.type(InvocationResult.class); return PROJECT_MAPPER.readValue(json, javaType); } }