Package net.spy.memcached.vbucket

Source Code of net.spy.memcached.vbucket.ConfigurationProviderHTTP

package net.spy.memcached.vbucket;

import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.net.URI;
import java.net.InetSocketAddress;
import java.net.URLConnection;
import java.net.HttpURLConnection;
import java.net.Authenticator;
import java.net.URL;
import java.io.IOException;
import java.io.InputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.text.ParseException;

import net.spy.memcached.AddrUtil;
import net.spy.memcached.compat.SpyObject;
import net.spy.memcached.vbucket.config.Bucket;
import net.spy.memcached.vbucket.config.Config;
import net.spy.memcached.vbucket.config.Pool;
import net.spy.memcached.vbucket.config.ConfigurationParserJSON;
import net.spy.memcached.vbucket.config.ConfigurationParser;

public class ConfigurationProviderHTTP extends SpyObject implements ConfigurationProvider {
/**
* Configuration management class that provides methods for retrieving vbucket configuration and receiving
* configuration updates.
*/
    private static final String DEFAULT_POOL_NAME = "default";
    private static final String ANONYMOUS_AUTH_BUCKET = "default";
    /**
     * The specification version which this client meets.  This will be included
     * in requests to the server.
     */
    public static final String CLIENT_SPEC_VER = "1.0";
    private List<URI> baseList;
    private String restUsr;
    private String restPwd;
    private URI loadedBaseUri;
    // map of <bucketname, bucket> currently loaded
    private Map<String, Bucket> buckets = new ConcurrentHashMap<String, Bucket>();

    // map of <poolname, pool> currently loaded
    //private Map<String, Pool> pools = new ConcurrentHashMap<String, Pool>();
    private ConfigurationParser configurationParser = new ConfigurationParserJSON();
    private Map<String, BucketMonitor> monitors = new HashMap<String, BucketMonitor>();

    /**
     * Constructs a configuration provider with disabled authentication for the REST service
     * @param baseList list of urls to treat as base
     * @throws IOException
     */
    public ConfigurationProviderHTTP(List<URI> baseList) throws IOException {
        this(baseList, null, null);
    }

    /**
     * Constructs a configuration provider with a given credentials for the REST service
     * @param baseList list of urls to treat as base
     * @param restUsr username
     * @param restPwd password
     * @throws IOException
     */
    public ConfigurationProviderHTTP(List<URI> baseList, String restUsr, String restPwd) throws IOException {
        this.baseList = baseList;
        this.restUsr = restUsr;
        this.restPwd = restPwd;
    }

    /**
     * Connects to the REST service and retrieves the bucket configuration from the first pool available
     * @param bucketname bucketname
     * @return vbucket configuration
     * @throws ConfigurationException
     */
    public Bucket getBucketConfiguration(final String bucketname) throws ConfigurationException {
        if (bucketname == null || bucketname.isEmpty()) {
            throw new IllegalArgumentException("Bucket name can not be blank.");
        }
        Bucket bucket = this.buckets.get(bucketname);
        if (bucket == null) {
            readPools(bucketname);
        }
        return this.buckets.get(bucketname);
    }

    /**
     * For a given bucket to be found, walk the URIs in the baselist until the
     * bucket needed is found.
     *
     * @param bucketToFind
     * @throws ConfigurationException
     */
    private void readPools(String bucketToFind) throws ConfigurationException {
  // the intent with this method is to encapsulate all of the walking of URIs
  // and populating an internal object model of the configuration to one place
        for (URI baseUri : baseList) {
            try {
                // get and parse the response from the current base uri
                URLConnection baseConnection = urlConnBuilder(null, baseUri);
                String base = readToString(baseConnection);
                if ("".equals(base)) {
                    getLogger().warn("Provided URI " + baseUri + " has an empty response... skipping");
                    continue;
                }
                Map<String, Pool> pools = this.configurationParser.parseBase(base);

                // check for the default pool name
                if (!pools.containsKey(DEFAULT_POOL_NAME)) {
                    getLogger().warn("Provided URI " + baseUri + " has no default pool... skipping");
                    continue;
                }
                // load pools
                for (Pool pool : pools.values()) {
                    URLConnection poolConnection = urlConnBuilder(baseUri, pool.getUri());
                    String poolString = readToString(poolConnection);
                    configurationParser.loadPool(pool, poolString);
                    URLConnection poolBucketsConnection = urlConnBuilder(baseUri, pool.getBucketsUri());
                    String sBuckets = readToString(poolBucketsConnection);
                    Map<String, Bucket> bucketsForPool = configurationParser.parseBuckets(sBuckets);
                    pool.replaceBuckets(bucketsForPool);

                }
                // did we found our bucket?
                boolean bucketFound = false;
                for (Pool pool : pools.values()) {
                    if (pool.hasBucket(bucketToFind)) {
                        bucketFound = true;
      break;
                    }
                }
                if (bucketFound) {
                    for (Pool pool : pools.values()) {
                        for (Map.Entry<String, Bucket> bucketEntry : pool.getROBuckets().entrySet()) {
                            this.buckets.put(bucketEntry.getKey(), bucketEntry.getValue());
                        }
                    }
                    this.loadedBaseUri = baseUri;
                    return;
                }
            } catch (ParseException e) {
                getLogger().warn("Provided URI " + baseUri + " has an unparsable response...skipping", e);
            } catch (IOException e) {
                getLogger().warn("Connection problems with URI " + baseUri + " ...skipping", e);
            }
            throw new ConfigurationException("Configuration for bucket " + bucketToFind + " was not found.");
        }
    }

    public List<InetSocketAddress> getServerList(final String bucketname) throws ConfigurationException {
        Bucket bucket = getBucketConfiguration(bucketname);
        List<String> servers = bucket.getConfig().getServers();
        StringBuilder serversString = new StringBuilder();
        for (String server : servers) {
            serversString.append(server).append(' ');
        }
        return AddrUtil.getAddresses(serversString.toString());
    }

    /**
     * Subscribes for configuration updates
     * @param bucketName bucket name to receive configuration for
     * @param rec reconfigurable that will receive updates
     * @throws ConfigurationException
     */
    public void subscribe(String bucketName, Reconfigurable rec) throws ConfigurationException {
        Bucket bucket = getBucketConfiguration(bucketName);

        ReconfigurableObserver obs = new ReconfigurableObserver(rec);
        BucketMonitor monitor = this.monitors.get(bucketName);
        if (monitor == null) {
            URI streamingURI = bucket.getStreamingURI();
            monitor = new BucketMonitor(this.loadedBaseUri.resolve(streamingURI), bucketName, this.restUsr, this.restPwd, configurationParser);
            this.monitors.put(bucketName, monitor);
            monitor.addObserver(obs);
            monitor.startMonitor();
        } else {
            monitor.addObserver(obs);
        }
    }

    /**
     * Unsubscribe from updates on a given bucket and given reconfigurable
     * @param vbucketName bucket name
     * @param rec reconfigurable
     */
    public void unsubscribe(String vbucketName, Reconfigurable rec) {
        BucketMonitor monitor = this.monitors.get(vbucketName);
        if (monitor != null) {
            monitor.deleteObserver(new ReconfigurableObserver(rec));
        }
    }

    public Config getLatestConfig(String bucketname) throws ConfigurationException {
        Bucket bucket = getBucketConfiguration(bucketname);
        return bucket.getConfig();
    }

    public String getAnonymousAuthBucket() {
        return ANONYMOUS_AUTH_BUCKET;
    }

    /**
     * Shutdowns a monitor connections to the REST service
     */
    public void shutdown() {
        for (BucketMonitor monitor : this.monitors.values()) {
            monitor.shutdown();
        }
    }

    /**
     * Create a URL which has the appropriate headers to interact with the
     * service.  Most exception handling is up to the caller.
     *
     * @param resource the URI either absolute or relative to the base for this ClientManager
     * @return
     * @throws java.io.IOException
     */
    private URLConnection urlConnBuilder(URI base, URI resource) throws IOException {
        if (!resource.isAbsolute() && base != null) {
            resource = base.resolve(resource);
        }
        URL specURL = resource.toURL();
        URLConnection connection = specURL.openConnection();
        connection.setRequestProperty("Accept", "application/json");
        connection.setRequestProperty("user-agent", "spymemcached vbucket client");
        connection.setRequestProperty("X-memcachekv-Store-Client-Specification-Version", CLIENT_SPEC_VER);
  if (restUsr != null) {
      connection.setRequestProperty("Authorization", buildAuthHeader(restUsr, restPwd));
  }

        return connection;

    }

    /**
     * Helper method that reads content from url connection to the string
     * @param connection a given url connection
     * @return content string
     * @throws IOException
     */
    private String readToString(URLConnection connection) throws IOException {
  BufferedReader reader = null;
  try {
    InputStream inStream = connection.getInputStream();
    if (connection instanceof java.net.HttpURLConnection) {
      HttpURLConnection httpConnection = (HttpURLConnection) connection;
      if (httpConnection.getResponseCode() == 403) {
        throw new IOException("Service does not accept the authentication credentials: "
          + httpConnection.getResponseCode() + httpConnection.getResponseMessage());
      } else if (httpConnection.getResponseCode() >= 400) {
        throw new IOException("Service responded with a failure code: "
          + httpConnection.getResponseCode() + httpConnection.getResponseMessage());
      }
    } else {
      throw new IOException("Unexpected URI type encountered");
    }
    reader = new BufferedReader(new InputStreamReader(inStream));
    String str;
    StringBuilder buffer = new StringBuilder();
    while ((str = reader.readLine()) != null) {
      buffer.append(str);
    }
    return buffer.toString();
      } finally {
      reader.close();
  }
    }

    /**
     * Oddly, lots of things that do HTTP seem to not know how to do this and
     * Authenticator caches for the process.  Since we only need Basic at the
     * moment simply, add the header.
     *
     * @return a value for an HTTP Basic Auth Header
     */
    protected static String buildAuthHeader(String username, String password) {
        // apparently netty isn't familiar with HTTP Basic Auth
        StringBuilder clearText = new StringBuilder(username);
        clearText.append(':');
        if (password != null) {
            clearText.append(password);
        }
        // and apache base64 codec has extra \n\l we have to strip off
        String encodedText = org.apache.commons.codec.binary.Base64.encodeBase64String(clearText.toString().getBytes());
        char[] encodedWoNewline = new char[encodedText.length() - 2];
        encodedText.getChars(0, encodedText.length() - 2, encodedWoNewline, 0);
        String authVal = "Basic " + new String(encodedWoNewline);

        return authVal;
    }

}
TOP

Related Classes of net.spy.memcached.vbucket.ConfigurationProviderHTTP

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.