com.outerspacecat.memcache.StaticContinuum.java Source code

Java tutorial

Introduction

Here is the source code for com.outerspacecat.memcache.StaticContinuum.java

Source

/**
 * Copyright 2011 Caleb Richardson
 * 
 * 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 com.outerspacecat.memcache;

import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.outerspacecat.connection.DataSource;
import com.outerspacecat.connection.NamedDataSource;
import com.outerspacecat.util.Utils;
import java.util.NavigableMap;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.ThreadSafe;

/**
 * Provides lookup of Memcache nodes by key based on a static collection of
 * named data sources. Utilizes consistent hashing.
 * 
 * @author Caleb Richardson
 */
@Immutable
@ThreadSafe
public final class StaticContinuum implements Continuum {
    private final static int NUM_REPLICAS = 150;

    private final NavigableMap<Integer, DataSource<MemcacheConnection>> continuum;

    /**
     * Create a new continuum.
     * 
     * @param nodes the Memcache nodes to use. Must be non {@code null}, must
     *        contain one or more elements, and each element must be non
     *        {@code null}.
     */
    public StaticContinuum(final Iterable<? extends NamedDataSource<MemcacheConnection>> nodes) {
        Preconditions.checkNotNull(nodes, "nodes required");
        Preconditions.checkArgument(nodes.iterator().hasNext(), "nodes must contain at least one element");
        for (NamedDataSource<MemcacheConnection> node : nodes)
            Preconditions.checkNotNull(node, "each nodes element must be non null");

        continuum = Maps.newTreeMap();

        for (NamedDataSource<MemcacheConnection> node : nodes) {
            continuum.put(hash(node.getName()), node);
            for (int i = 0; i < NUM_REPLICAS; ++i)
                continuum.put(hash(node.getName() + "_" + i), node);
        }
    }

    @Override
    public DataSource<MemcacheConnection> getNode(final String key) {
        DataSource<MemcacheConnection> node = continuum.ceilingEntry(hash(key)).getValue();

        if (node == null)
            node = continuum.firstEntry().getValue();

        return node;
    }

    private int hash(final String key) {
        byte[] md5 = Utils.md5(key.getBytes(Charsets.US_ASCII));

        return (md5[0] & 0xFF) | ((md5[1] & 0xFF) << 8) | ((md5[2] & 0xFF) << 16) | ((md5[3] & 0xFF) << 24);
    }
}