All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.basho.riak.client.util.ClientUtils Maven / Gradle / Ivy

/*
 * This file is provided to you 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 com.basho.riak.client.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Map.Entry;

import org.apache.commons.codec.binary.Base64;
import org.apache.http.Header;
import org.apache.http.client.HttpClient;
import org.apache.http.client.params.AllClientPNames;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.HttpParams;
import org.json.JSONArray;
import org.json.JSONObject;

import com.basho.riak.client.RiakClient;
import com.basho.riak.client.RiakConfig;
import com.basho.riak.client.RiakLink;
import com.basho.riak.client.RiakObject;
import com.basho.riak.client.response.RiakExceptionHandler;
import com.basho.riak.client.response.RiakIORuntimeException;

/**
 * Utility functions.
 *
 * @deprecated with the addition of a protocol buffers client in 0.14 all the
 *             existing REST client code should be in client.http.* this class
 *             has therefore been moved. Please use
 *             com.basho.riak.client.http.util.ClientUtils
 *             instead.
 *             

WARNING: This class will be REMOVED in the next version.

* @see com.basho.riak.client.http.util.ClientUtils */ @Deprecated public class ClientUtils { // Matches the scheme, host and port of a URL private static String URL_PATH_MASK = "^(?:[A-Za-z0-9+-\\.]+://)?[^/]*"; private static Random rng = new Random(); /** * Construct a new {@link HttpClient} instance given a {@link RiakConfig}. * * @param config * {@link RiakConfig} containing HttpClient configuration * specifics. * @return A new {@link HttpClient} */ public static HttpClient newHttpClient(RiakConfig config) { HttpClient http = config.getHttpClient(); ClientConnectionManager m; if (http == null) { m = new ThreadSafeClientConnManager(); if (config.getMaxConnections() != null) { ((ThreadSafeClientConnManager) m).setMaxTotal(config.getMaxConnections()); ((ThreadSafeClientConnManager) m).setDefaultMaxPerRoute(config.getMaxConnections()); } http = new DefaultHttpClient(m); if (config.getRetryHandler() != null) { ((DefaultHttpClient) http).setHttpRequestRetryHandler(config.getRetryHandler()); } } else { m = http.getConnectionManager(); } HttpParams cp = http.getParams(); if (config.getTimeout() != null) { cp.setIntParameter(AllClientPNames.CONNECTION_TIMEOUT, config.getTimeout()); cp.setIntParameter(AllClientPNames.SO_TIMEOUT, config.getTimeout()); } return http; } /** * Return a URL to the given bucket * * @param config * RiakConfig containing the base URL to Riak * @param bucket * Bucket whose URL to retrieving * @return URL to the bucket */ public static String makeURI(RiakConfig config, String bucket) { return config.getUrl() + "/" + urlEncode(bucket); } /** * Return a URL to the given object * * @param config * RiakConfig containing the base URL to Riak * @param bucket * Bucket of the object * @param key * Key of the object * @return URL to the object */ public static String makeURI(RiakConfig config, String bucket, String key) { if (key == null) return makeURI(config, bucket); return makeURI(config, bucket) + "/" + urlEncode(key); } /** * Return a URL to the given object * * @param config * RiakConfig containing the base URL to Riak * @param bucket * Bucket of the object * @param key * Key of the object * @param extra * Extra path information beyond the bucket and key (e.g. for * link walking or query parameters) * @return URL to the object */ public static String makeURI(RiakConfig config, String bucket, String key, String extra) { if (extra == null) return makeURI(config, bucket, key); if (!extra.startsWith("?") && !extra.startsWith("/")) { extra = "/" + extra; } return makeURI(config, bucket, key) + extra; } /** * Return just the path portion of the given URL */ public static String getPathFromUrl(String url) { if (url == null) return null; return url.replaceFirst(URL_PATH_MASK, ""); } /** * UTF-8 encode the string */ public static String urlEncode(String s) { try { return URLEncoder.encode(s, "UTF-8"); } catch (UnsupportedEncodingException unreached) { // UTF-8 must be supported by every Java implementation: // http://java.sun.com/j2se/1.4.2/docs/api/java/nio/charset/Charset.html throw new IllegalStateException("UTF-8 must be supported", unreached); } } /** * Decodes a UTF-8 encoded string */ public static String urlDecode(String s) { try { return URLDecoder.decode(s, "UTF-8"); } catch (UnsupportedEncodingException unreached) { throw new IllegalStateException("UTF-8 must be supported", unreached); } } /** * Base64 encodes the first 4 bytes of clientId into a value acceptable for * the X-Riak-ClientId header. */ public static String encodeClientId(byte[] clientId) { if (clientId == null || clientId.length < 4) throw new IllegalArgumentException("ClientId must be at least 4 bytes"); try { return new String(Base64.encodeBase64(new byte[] { clientId[0], clientId[1], clientId[2], clientId[3] }), "UTF-8"); } catch (UnsupportedEncodingException e) { throw new IllegalStateException("UTF-8 support is required by JVM"); } } public static String encodeClientId(String clientId) { return encodeClientId(CharsetUtils.asBytes(clientId, CharsetUtils.ISO_8859_1)); } /** * Returns a random X-Riak-ClientId header value. */ public static String randomClientId() { byte[] rnd = new byte[4]; rng.nextBytes(rnd); return encodeClientId(rnd); } /** * Unquote and unescape an HTTP quoted-string: * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 * * Does nothing if s is not quoted. * * @param s * quoted-string to unquote * @return s with quotes and backslash-escaped characters unescaped */ public static String unquoteString(String s) { if (s.startsWith("\"") && s.endsWith("\"")) { s = s.substring(1, s.length() - 1); } return s.replaceAll("\\\\(.)", "$1"); } /** * Convert a header array returned from {@link HttpClient} to a map * * @param headers * Header array returned from HttpClient * @return Map of the header names to values */ public static Map asHeaderMap(Header[] headers) { Map m = new HashMap(); if (headers != null) { for (Header header : headers) { m.put(header.getName().toLowerCase(), header.getValue()); } } return m; } /** * Convert a {@link JSONObject} to a map * * @param json * {@link JSONObject} to convert * @return Map of the field names to string representations of the values */ @SuppressWarnings("rawtypes") public static Map jsonObjectAsMap(JSONObject json) { if (json == null) return null; Map m = new HashMap(); for (Iterator iter = json.keys(); iter.hasNext();) { Object obj = iter.next(); if (obj != null) { String key = obj.toString(); m.put(key, json.optString(key)); } } return m; } /** * Convert a {@link JSONArray} to a list * * @param json * {@link JSONArray} to convert * @return List of string representations of the elements */ public static List jsonArrayAsList(JSONArray json) { if (json == null) return null; List l = new ArrayList(); for (int i = 0; i < json.length(); i++) { l.add(json.optString(i)); } return l; } /** * Join the elements in arr in to a single string separated by delimiter. */ public static String join(String[] arr, String delimiter) { StringBuffer buf = new StringBuffer(); if (arr == null || arr.length == 0) return null; buf.append(arr[0]); for (int i = 1; i < arr.length; i++) { buf.append(delimiter); buf.append(arr[i]); } return buf.toString(); } /** * Copies data from an {@link InputStream} to an {@link OutputStream} in * blocks * * @param in * InputStream to copy * @param out * OutputStream to copy to * @throws IOException */ public static void copyStream(InputStream in, OutputStream out) throws IOException { byte[] buffer = new byte[1024]; while (true) { final int readCount = in.read(buffer); if (readCount == -1) { break; } out.write(buffer, 0, readCount); } } /** * Parse a link header into a {@link RiakLink}. See {@link LinkHeader}. * * @param header * The HTTP Link header value. * @return List of {@link RiakLink} objects constructed from the links in * header in order. */ public static List parseLinkHeader(String header) { List links = new ArrayList(); Map> parsedLinks = LinkHeader.parse(header); for (Entry> e: parsedLinks.entrySet()) { String url = e.getKey(); RiakLink link = parseOneLink(url, e.getValue()); if (link != null) { links.add(link); } } return links; } /** * Create a {@link RiakLink} object from a single parsed link from the Link * header * * @param url * The link URL * @param params * The link parameters * @return {@link RiakLink} object */ private static RiakLink parseOneLink(String url, Map params) { String tag = params.get(Constants.LINK_TAG); if (tag != null) { String[] parts = url.split("/"); if (parts.length >= 2) return new RiakLink(parts[parts.length - 2], parts[parts.length - 1], tag); } return null; } /** * Extract only the user-specified metadata headers from a header set: all * headers prefixed with X-Riak-Meta-. The prefix is removed before * returning. * * @param headers * The full HTTP header set from the response * @return Map of all headers prefixed with X-Riak-Meta- with prefix * removed. */ public static Map parseUsermeta(Map headers) { Map usermeta = new HashMap(); if (headers != null) { for (Entry e : headers.entrySet()) { String header = e.getKey(); if (header != null && header.toLowerCase().startsWith(Constants.HDR_USERMETA_PREFIX)) { usermeta.put(header.substring(Constants.HDR_USERMETA_PREFIX.length()), e.getValue()); } } } return usermeta; } /** * Convert a multipart/mixed document to a list of {@link RiakObject}s. * * @param riak * {@link RiakClient} this object should be associate with, or * null if none * @param bucket * original object's bucket * @param key * original object's key * @param docHeaders * original document's headers * @param docBody * original document's body * @return List of {@link RiakObject}s represented by the multipart document */ public static List parseMultipart(RiakClient riak, String bucket, String key, Map docHeaders, byte[] docBody) { String vclock = null; boolean siblingVclock = false; if (docHeaders != null) { vclock = docHeaders.get(Constants.HDR_VCLOCK); if( vclock != null) { siblingVclock = true; } } List parts = Multipart.parse(docHeaders, docBody); List objects = new ArrayList(); if (parts != null) { for (Multipart.Part part : parts) { Map headers = part.getHeaders(); // handles the case of link walk multi part responses where the vclock header is in the part not the top response if (!siblingVclock) { vclock = headers.get(Constants.HDR_VCLOCK); } if(vclock == null) { // this should never happen // exception here to shorten path from bug occurrence // to bug manifestation throw new IllegalStateException("no vclock found"); } List links = parseLinkHeader(headers.get(Constants.HDR_LINK)); Map usermeta = parseUsermeta(headers); String location = headers.get(Constants.HDR_LOCATION); String partBucket = bucket; String partKey = key; if (location != null) { String[] locationParts = location.split("/"); if (locationParts.length >= 2) { partBucket = locationParts[locationParts.length - 2]; partKey = locationParts[locationParts.length - 1]; } } RiakObject o = new RiakObject(riak, partBucket, partKey, part.getBody(), headers.get(Constants.HDR_CONTENT_TYPE), links, usermeta, vclock, headers.get(Constants.HDR_LAST_MODIFIED), headers.get(Constants.HDR_ETAG)); objects.add(o); } } return objects; } /** * Throws a checked {@link Exception} not declared in the method signature, * which can be particularly useful for throwing checked exceptions within a * {@link RiakExceptionHandler}. Clearly, this circumvents compiler * safeguards, so use with caution. You've been warned. * * @param exception * A checked (or unchecked) exception to be thrown. */ public static void throwChecked(final Throwable exception) { new CheckedThrower().throwChecked(exception); } /** * Buffers an input stream into a byte array * @param valueStream the stream to read into an array * @return the byte array of the consumed stream */ public static byte[] bufferStream(InputStream valueStream) { if (valueStream == null) { return new byte[] {}; } try { ByteArrayOutputStream tmp = new ByteArrayOutputStream(); byte[] data = new byte[4096]; int l = 0; while ((l = valueStream.read(data)) >= 0) { tmp.write(data, 0, l); } return tmp.toByteArray(); } catch (IOException e) { throw new RiakIORuntimeException(e); } } } class CheckedThrower { @SuppressWarnings("unchecked") public void throwChecked(Throwable exception) throws T { throw (T) exception; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy