Package org.slim3.memcache

Source Code of org.slim3.memcache.MemcacheDelegate$CacheKey

/*
* Copyright 2004-2010 the Seasar Foundation and the Others.
*
* 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.slim3.memcache;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import com.google.appengine.api.NamespaceManager;
import com.google.appengine.api.memcache.ErrorHandler;
import com.google.appengine.api.memcache.Expiration;
import com.google.appengine.api.memcache.InvalidValueException;
import com.google.appengine.api.memcache.MemcacheSerialization;
import com.google.appengine.api.memcache.MemcacheService;
import com.google.appengine.api.memcache.MemcacheServiceException;
import com.google.appengine.api.memcache.MemcacheServiceFactory;
import com.google.appengine.api.memcache.MemcacheServicePb;
import com.google.appengine.api.memcache.Stats;
import com.google.appengine.api.memcache.MemcacheService.SetPolicy;
import com.google.appengine.repackaged.com.google.protobuf.ByteString;
import com.google.appengine.repackaged.com.google.protobuf.InvalidProtocolBufferException;
import com.google.appengine.repackaged.com.google.protobuf.Message;
import com.google.apphosting.api.ApiProxy;
import com.google.apphosting.api.ApiProxy.CapabilityDisabledException;

/**
* A delegate to access memcache service.
*
* @author higa
* @since 1.0.0
*
*/
public class MemcacheDelegate {

    private static final Logger logger =
        Logger.getLogger(MemcacheDelegate.class.getName());

    /**
     * The memcache service.
     */
    protected MemcacheService ms;

    /**
     * Constructor.
     */
    public MemcacheDelegate() {
        ms = MemcacheServiceFactory.getMemcacheService();
        ms.setErrorHandler(new S3ErrorHandler());
    }

    /**
     * Empties the cache of all values. Statistics are not affected. Note that
     * clearAll() does not respect namespaces - this flushes the cache for every
     * namespace.
     *
     * @throws CapabilityDisabledException
     *             if memcache service is disable
     */
    public void cleanAll() throws CapabilityDisabledException {
        ms.clearAll();
    }

    /**
     * Tests whether a given value is in cache, even if its value is null.
     *
     * @param key
     *            the key
     * @return whether a given value is in cache
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     * @throws CapabilityDisabledException
     *             if memcache service is disable
     */
    public boolean contains(Object key) throws IllegalArgumentException,
            CapabilityDisabledException {
        return ms.contains(key);
    }

    /**
     * Removes key from the cache.
     *
     * @param key
     *            the key
     * @return true if an entry existed to delete
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     * @throws CapabilityDisabledException
     *             if memcache service is disable
     */
    public boolean delete(Object key) throws IllegalArgumentException,
            CapabilityDisabledException {
        return ms.delete(key);
    }

    /**
     * Removes the given key from the cache, and prevents it from being added
     * under the MemcacheService.SetPolicy.ADD_ONLY_IF_NOT_PRESENT policy for
     * millisNoReAdd milliseconds thereafter. Calls to a put(java.lang.Object,
     * java.lang.Object, com.google.appengine.api.memcache.Expiration,
     * com.google.appengine.api.memcache.MemcacheService.SetPolicy) method using
     * MemcacheService.SetPolicy.SET_ALWAYS are not blocked, however.
     *
     * @param key
     *            the key
     * @param millisNoReAdd
     *            the time during which calls to put using ADD_IF_NOT_PRESENT
     *            should be denied
     * @return true if an entry existed to delete
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     * @throws CapabilityDisabledException
     *             if memcache service is disable
     */
    public boolean delete(Object key, long millisNoReAdd)
            throws IllegalArgumentException, CapabilityDisabledException {
        return ms.delete(key, millisNoReAdd);
    }

    /**
     * Removes keys from the cache.
     *
     * @param keys
     *            the keys for entries to delete.
     * @return the Set of keys deleted. Any keys in keys but not in the returned
     *         set were not found in the cache.
     * @throws NullPointerException
     *             the keys parameter is null
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     * @throws CapabilityDisabledException
     *             if memcache service is disable
     *
     */
    public Set<Object> deleteAll(Iterable<?> keys) throws NullPointerException,
            IllegalArgumentException, CapabilityDisabledException {
        Collection<Object> keys2 = toCollection(keys);
        return ms.deleteAll(keys2);
    }

    /**
     * Removes keys from the cache.
     *
     * @param keys
     *            the keys for entries to delete.
     * @param millisNoReAdd
     *            the time during which calls to put using ADD_IF_NOT_PRESENT
     *            should be denied
     * @return the Set of keys deleted. Any keys in keys but not in the returned
     *         set were not found in the cache.
     * @throws NullPointerException
     *             the keys parameter is null
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     * @throws CapabilityDisabledException
     *             if memcache service is disable
     *
     */
    public Set<Object> deleteAll(Iterable<?> keys, long millisNoReAdd)
            throws NullPointerException, IllegalArgumentException,
            CapabilityDisabledException {
        Collection<Object> keys2 = toCollection(keys);
        return ms.deleteAll(keys2, millisNoReAdd);
    }

    /**
     * Returns a previously-stored value, or null if unset. Since null might be
     * the set value in some cases, so we also have contains(Object) which
     * returns boolean.
     *
     * @param key
     *            the key
     * @return a previously-stored value
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     * @throws CapabilityDisabledException
     *             if memcache service is disable
     */
    public Object get(Object key) throws IllegalArgumentException,
            CapabilityDisabledException {
        return getInternal(key);
    }

    /**
     * Returns a previously-stored value, or null if unset. Since null might be
     * the set value in some cases, so we also have contains(Object) which
     * returns boolean.
     *
     * @param key
     *            the key
     * @return a previously-stored value
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     */
    protected Object getInternal(Object key) {
        MemcacheServicePb.MemcacheGetResponse.Builder response =
            MemcacheServicePb.MemcacheGetResponse.newBuilder();
        MemcacheServicePb.MemcacheGetRequest request;
        try {
            request =
                MemcacheServicePb.MemcacheGetRequest
                    .newBuilder()
                    .setNameSpace(getNamespace())
                    .addKey(
                        ByteString.copyFrom(MemcacheSerialization
                            .makePbKey(key)))
                    .build();
        } catch (IOException ex) {
            throw new IllegalArgumentException(
                (new StringBuilder()).append("Cannot use as a key: '").append(
                    key).append("'").toString(),
                ex);
        }
        if (!makeSyncCall("Get", request, response, (new StringBuilder())
            .append("Memcache get: exception getting 1 key (")
            .append(key)
            .append(")")
            .toString()))
            return null;
        if (response.getItemCount() == 0)
            return null;
        MemcacheServicePb.MemcacheGetResponse.Item item = response.getItem(0);
        try {
            return S3MemcacheSerialization.deserialize(item
                .getValue()
                .toByteArray(), item.getFlags());
        } catch (ClassNotFoundException ex) {
            ms.getErrorHandler().handleDeserializationError(
                new InvalidValueException((new StringBuilder()).append(
                    "Can't find class for value of key '").append(key).append(
                    "'").toString(), ex));
        } catch (IOException ex) {
            throw new InvalidValueException((new StringBuilder())
                .append("IO exception parsing value of '")
                .append(key)
                .append("'")
                .toString(), ex);
        }
        return null;
    }

    /**
     * Returns the namespace.
     *
     * @return the namespace
     */
    protected String getNamespace() {
        String namespace = NamespaceManager.get();
        return namespace != null ? namespace : "";
    }

    /**
     * Performs a get of multiple keys at once. This is more efficient than
     * multiple separate calls to get(Object), and allows a single call to both
     * test for contains(Object) and also fetch the value, because the return
     * will not include mappings for keys not found.
     *
     * @param keys
     *            the keys.
     * @return a mapping from keys to values of any entries found. If a
     *         requested key is not found in the cache, the key will not be in
     *         the returned Map.
     * @throws NullPointerException
     *             if the keys parameter is null
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     * @throws CapabilityDisabledException
     *             if memcache service is disable
     */
    public Map<Object, Object> getAll(Iterable<?> keys)
            throws NullPointerException, IllegalArgumentException,
            CapabilityDisabledException {
        return getAllInternal(keys);
    }

    /**
     * Performs a get of multiple keys at once. This is more efficient than
     * multiple separate calls to get(Object), and allows a single call to both
     * test for contains(Object) and also fetch the value, because the return
     * will not include mappings for keys not found.
     *
     * @param keys
     *            the keys.
     * @return a mapping from keys to values of any entries found. If a
     *         requested key is not found in the cache, the key will not be in
     *         the returned Map.
     * @throws NullPointerException
     *             if the keys parameter is null
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     */
    @SuppressWarnings("unchecked")
    protected Map<Object, Object> getAllInternal(Iterable<?> keys)
            throws NullPointerException, IllegalArgumentException {
        if (keys == null) {
            throw new NullPointerException(
                "The keys parameter must not be null.");
        }
        MemcacheServicePb.MemcacheGetResponse.Builder response =
            MemcacheServicePb.MemcacheGetResponse.newBuilder();
        MemcacheServicePb.MemcacheGetRequest.Builder requestBuilder =
            MemcacheServicePb.MemcacheGetRequest.newBuilder().setNameSpace(
                getNamespace());
        Map<CacheKey, Object> cacheKeyToObjectKey =
            new HashMap<CacheKey, Object>();
        for (Object key : keys) {
            try {
                byte keybytes[] = MemcacheSerialization.makePbKey(key);
                cacheKeyToObjectKey.put(new CacheKey(keybytes), key);
                requestBuilder.addKey(ByteString.copyFrom(keybytes));
            } catch (IOException ex) {
                throw new IllegalArgumentException((new StringBuilder())
                    .append("Cannot use as key: '")
                    .append(key)
                    .append("'")
                    .toString(), ex);
            }
        }
        if (!makeSyncCall(
            "Get",
            requestBuilder.build(),
            response,
            "Memcache get: exception getting multiple keys"))
            return Collections.emptyMap();
        Map result = new HashMap();
        Iterator i = response.getItemList().iterator();
        do {
            if (!i.hasNext()) {
                break;
            }
            MemcacheServicePb.MemcacheGetResponse.Item item =
                (MemcacheServicePb.MemcacheGetResponse.Item) i.next();
            Object key = null;
            try {
                key =
                    cacheKeyToObjectKey.get(new CacheKey(item
                        .getKey()
                        .toByteArray()));
                Object obj =
                    S3MemcacheSerialization.deserialize(item
                        .getValue()
                        .toByteArray(), item.getFlags());
                result.put(key, obj);
            } catch (ClassNotFoundException ex) {
                ms.getErrorHandler().handleDeserializationError(
                    new InvalidValueException((new StringBuilder())
                        .append("Can't find class for value of key '")
                        .append(key)
                        .append("'")
                        .toString(), ex));
                return null;
            } catch (IOException ex) {
                throw new InvalidValueException((new StringBuilder())
                    .append("IO exception parsing value of '")
                    .append(key)
                    .append("'")
                    .toString(), ex);
            }
        } while (true);
        return result;
    }

    /**
     * Atomically fetches, increments, and stores a given integral value.
     * "Integral" types are Byte, Short, Integer, Long, and in some cases String
     * (if the string is parseable as a number, for example via
     * Long.parseLong(String). The entry must already exist, and have a
     * non-negative value.
     *
     * Incrementing by positive amounts will reach signed 64-bit max (2^63 - 1)
     * and then wrap-around to signed 64-bit min (-2^63), continuing increments
     * from that point.
     *
     * To facilitate use as an atomic countdown, incrementing by a negative
     * value (i.e. decrementing) will not go below zero: incrementing 2 by -5
     * will return 0, not -3. However, due to the way numbers are stored,
     * decrementing -3 by -5 will result in -8; so the zero-floor rule only
     * applies to decrementing numbers that were positive.
     *
     * Note: The actual representation of all numbers in Memcache is a string.
     * This means if you initially stored a number as a string (e.g., "10") and
     * then increment it, everything will work properly, including wrapping
     * beyond signed 64-bit int max. However, if you get the key past the point
     * of wrapping, you will receive an unsigned integer value, not a signed
     * integer value.
     *
     * @param key
     *            the key
     * @param delta
     *            the size of the increment, positive or negative.
     * @return the post-increment value, as a long. However, a get(Object) of
     *         the key will still have the original type (Byte, Short, etc.). If
     *         there is no entry for key, returns null.
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     * @throws InvalidValueException
     *             if the object incremented is not of a integral type
     * @throws CapabilityDisabledException
     *             if memcache service is disable
     */
    public Long increment(Object key, long delta)
            throws IllegalArgumentException, InvalidValueException,
            CapabilityDisabledException {
        return ms.increment(key, delta);
    }

    /**
     * Like normal increment, but allows for an optional initial value for the
     * key to take on if not already present in the cache.
     *
     * @param key
     *            the key
     * @param delta
     *            the size of the increment, positive or negative.
     * @param initialValue
     *            the value to insert into the cache if the key is not present
     * @return the post-increment value, as a long. However, a get(Object) of
     *         the key will still have the original type (Byte, Short, etc.). If
     *         there is no entry for key, returns null.
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     * @throws InvalidValueException
     *             if the object incremented is not of a integral type
     * @throws CapabilityDisabledException
     *             if memcache service is disable
     */
    public Long increment(Object key, long delta, long initialValue)
            throws IllegalArgumentException, InvalidValueException,
            CapabilityDisabledException {
        return ms.increment(key, delta, initialValue);
    }

    /**
     * Like normal increment, but increments a batch of separate keys in
     * parallel by the same delta.
     *
     * @param keys
     *            the keys
     * @param delta
     *            the size of the increment, positive or negative.
     * @return mapping keys to their new values; values will be null if they
     *         could not be incremented or were not present in the cache
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     * @throws CapabilityDisabledException
     *             if memcache service is disable
     */
    public Map<Object, Long> incrementAll(Iterable<?> keys, long delta)
            throws IllegalArgumentException, CapabilityDisabledException {
        return ms.incrementAll(toCollection(keys), delta);
    }

    /**
     * Like normal increment, but increments a batch of separate keys in
     * parallel by the same delta and potentially sets a starting value.
     *
     * @param keys
     *            the keys
     * @param delta
     *            the size of the increment, positive or negative.
     * @param initialValue
     *            the value to insert into the cache if the key is not present
     * @return mapping keys to their new values; values will be null if they
     *         could not be incremented for whatever reason
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     * @throws CapabilityDisabledException
     *             if memcache service is disable
     */
    public Map<Object, Long> incrementAll(Iterable<?> keys, long delta,
            long initialValue) throws IllegalArgumentException,
            CapabilityDisabledException {
        return ms.incrementAll(toCollection(keys), delta, initialValue);
    }

    /**
     * Like normal increment, but accepts a mapping of separate controllable
     * offsets for each key individually.
     *
     * @param offsets
     *            the offsets
     *
     * @return mapping keys to their new values; values will be null if they
     *         could not be incremented for whatever reason
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     * @throws CapabilityDisabledException
     *             if memcache service is disable
     */
    public Map<Object, Long> incrementAll(Map<Object, Long> offsets)
            throws IllegalArgumentException, CapabilityDisabledException {
        return ms.incrementAll(offsets);
    }

    /**
     * Like normal increment, but accepts a mapping of separate controllable
     * offsets for each key individually. Callers may also pass an initial value
     * for the keys to take on if they are not already present in the cache.
     *
     * @param offsets
     *            the offsets
     * @param initialValue
     *            the value to insert into the cache if the key is not present
     * @return mapping keys to their new values; values will be null if they
     *         could not be incremented for whatever reason
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     * @throws CapabilityDisabledException
     *             if memcache service is disable
     */
    public Map<Object, Long> incrementAll(Map<Object, Long> offsets,
            long initialValue) throws IllegalArgumentException,
            CapabilityDisabledException {
        return ms.incrementAll(offsets);
    }

    /**
     * A convenience shortcut, equivalent to put(key, value, null,
     * SetPolicy.SET_ALWAYS).
     *
     * @param key
     *            the key
     * @param value
     *            the value
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     * @throws CapabilityDisabledException
     *             if memcache service is disable
     */
    public void put(Object key, Object value) throws IllegalArgumentException,
            CapabilityDisabledException {
        ms.put(key, value);
    }

    /**
     * A convenience shortcut, equivalent to put(key, value, expires,
     * SetPolicy.SET_ALWAYS).
     *
     * @param key
     *            the key
     * @param value
     *            the value
     * @param expires
     *            expiration time for the new values, or null for no time-based
     *            expiration
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     * @throws CapabilityDisabledException
     *             if memcache service is disable
     */
    public void put(Object key, Object value, Expiration expires)
            throws IllegalArgumentException, CapabilityDisabledException {
        ms.put(key, value, expires);
    }

    /**
     * Store a new value into the cache, using key, but subject to the policy
     * regarding existing entries.
     *
     * @param key
     *            the key
     * @param value
     *            the value
     * @param expires
     *            expiration time for the new values, or null for no time-based
     *            expiration
     * @param policy
     *            Requests particular handling regarding pre-existing entries
     *            under the same key. This parameter must not be null.
     * @return true if a new entry was created, false if not because of the
     *         policy.
     * @throws NullPointerException
     *             if the policy parameter is null
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     * @throws CapabilityDisabledException
     *             if memcache service is disable
     */
    public boolean put(Object key, Object value, Expiration expires,
            SetPolicy policy) throws NullPointerException,
            IllegalArgumentException, CapabilityDisabledException {
        if (policy == null) {
            throw new NullPointerException(
                "The policy parameter must not be null.");
        }
        return ms.put(key, value, expires, policy);
    }

    /**
     * A convenience shortcut, equivalent to putAll(values, null,
     * SetPolicy.SET_ALWAYS).
     *
     * @param values
     *            key/value mappings for new entries to add to the cache
     * @throws NullPointerException
     *             if the values parameter is null
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     * @throws CapabilityDisabledException
     *             if memcache service is disable
     */
    public void putAll(Map<Object, Object> values) throws NullPointerException,
            IllegalArgumentException, CapabilityDisabledException {
        if (values == null) {
            throw new NullPointerException(
                "The values parameter must not be null.");
        }
        ms.putAll(values);
    }

    /**
     * A convenience shortcut, equivalent to putAll(values, expires,
     * SetPolicy.SET_ALWAYS).
     *
     * @param values
     *            key/value mappings for new entries to add to the cache
     * @param expires
     *            expiration time for the new values, or null for no time-based
     *            expiration
     * @throws NullPointerException
     *             if the values parameter is null
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     * @throws CapabilityDisabledException
     *             if memcache service is disable
     */
    public void putAll(Map<Object, Object> values, Expiration expires)
            throws NullPointerException, IllegalArgumentException,
            CapabilityDisabledException {
        if (values == null) {
            throw new NullPointerException(
                "The values parameter must not be null.");
        }
        ms.putAll(values, expires);
    }

    /**
     * A batch-processing variant of put(java.lang.Object, java.lang.Object,
     * com.google.appengine.api.memcache.Expiration,
     * com.google.appengine.api.memcache.MemcacheService.SetPolicy). This is
     * more efficiently implemented by the service than multiple calls.
     *
     * @param values
     *            key/value mappings for new entries to add to the cache
     * @param expires
     *            expiration time for the new values, or null for no time-based
     *            expiration
     * @param policy
     *            what to do if the entry is or is not already present
     * @return the set of keys for which entries were created. Keys in values
     *         may not be in the returned set because of the policy regarding
     *         pre-existing entries.
     * @throws NullPointerException
     *             if the values parameter is null or if the policy parameter is
     *             null
     * @throws IllegalArgumentException
     *             if the key cannot be serialized
     * @throws CapabilityDisabledException
     *             if memcache service is disable
     */
    public Set<Object> putAll(Map<Object, Object> values, Expiration expires,
            SetPolicy policy) throws NullPointerException,
            IllegalArgumentException, CapabilityDisabledException {
        if (values == null) {
            throw new NullPointerException(
                "The values parameter must not be null.");
        }
        if (policy == null) {
            throw new NullPointerException(
                "The policy parameter must not be null.");
        }
        return ms.putAll(values, expires, policy);
    }

    /**
     * Returns the current error handler.
     *
     * @return the current error handler.
     */
    public ErrorHandler errorHandler() {
        return ms.getErrorHandler();
    }

    /**
     * Sets the current error handler.
     *
     * @param errorHandler
     *            the error handler
     * @return this instance
     * @throws NullPointerException
     *             if the errorHandler parameter is null
     */
    public MemcacheDelegate errorHandler(ErrorHandler errorHandler)
            throws NullPointerException {
        if (errorHandler == null) {
            throw new NullPointerException(
                "The errorHandler parameter must not be null.");
        }
        ms.setErrorHandler(errorHandler);
        return this;
    }

    /**
     * Returns some statistics about the cache and its usage. Note that
     * statistics() does not respect namespaces - this will return stats for
     * every namespace. The response will never be null.
     *
     * @return some statistics
     */
    public Stats statistics() {
        return ms.getStatistics();
    }

    /**
     * Makes a sync call.
     *
     * @param methodName
     *            the method name.
     * @param request
     *            the request.
     * @param response
     *            the response.
     * @param errorText
     *            the error text.
     * @return whether this call succeeded
     */
    protected boolean makeSyncCall(String methodName, Message request,
            Message.Builder response, String errorText) {
        try {
            byte responseBytes[] =
                ApiProxy.makeSyncCall("memcache", methodName, request
                    .toByteArray());
            response.mergeFrom(responseBytes);
            return true;
        } catch (InvalidProtocolBufferException ex) {
            ms.getErrorHandler().handleServiceError(
                new MemcacheServiceException("Could not decode response:", ex));
        } catch (com.google.apphosting.api.ApiProxy.ApplicationException ae) {
            logger.info((new StringBuilder())
                .append(errorText)
                .append(": ")
                .append(ae.getErrorDetail())
                .toString());
            ms.getErrorHandler().handleServiceError(
                new MemcacheServiceException(errorText));
        } catch (com.google.apphosting.api.ApiProxy.ApiProxyException ex) {
            ms.getErrorHandler().handleServiceError(
                new MemcacheServiceException(errorText, ex));
        }
        return false;
    }

    /**
     * Converts the keys to a collection of keys.
     *
     * @param keys
     *            the keys.
     * @return a collection of the keys.
     * @throws NullPointerException
     *             if the keys parameter is null
     */
    @SuppressWarnings("unchecked")
    protected Collection<Object> toCollection(Iterable<?> keys)
            throws NullPointerException {
        if (keys == null) {
            throw new NullPointerException(
                "The keys parameter must not be null.");
        }
        if (keys instanceof Collection<?>) {
            return (Collection<Object>) keys;
        }
        Collection<Object> ret = new ArrayList<Object>();
        for (Object key : keys) {
            ret.add(key);
        }
        return ret;
    }

    /**
     * A key class for an array of bytes.
     *
     */
    protected static class CacheKey {

        /**
         * The array of bytes.
         */
        protected byte bytes[];

        /**
         * The cached hash code.
         */
        protected int hashCode;

        /**
         * Constructor.
         *
         * @param bytes
         *            the array of bytes
         */
        public CacheKey(byte bytes[]) {
            this.bytes = bytes;
            hashCode = Arrays.hashCode(bytes);
        }

        /**
         * Returns the array of bytes.
         *
         * @return the array of bytes
         */
        public byte[] getBytes() {
            return bytes;
        }

        @Override
        public boolean equals(Object other) {
            if (other instanceof CacheKey) {
                return Arrays.equals(bytes, ((CacheKey) other).bytes);
            }
            return false;
        }

        @Override
        public int hashCode() {
            return hashCode;
        }
    }
}
TOP

Related Classes of org.slim3.memcache.MemcacheDelegate$CacheKey

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.