org.springmodules.cache.impl.Element.java Source code

Java tutorial

Introduction

Here is the source code for org.springmodules.cache.impl.Element.java

Source

/*
 * Created on Apr 5, 2006
 *
 * 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.
 *
 * Copyright @2006 the original author or authors.
 */
package org.springmodules.cache.impl;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springmodules.util.Objects;

import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

/**
 * <p>
 * A cache element that stores <em>copies</em> of the given key and value.
 * </p>
 *
 * @author Omar Irbouh
 * @author Alex Ruiz
 */
public class Element implements Serializable, Cloneable {

    private static final long DEFAULT_TIME_TO_LIVE_MS = 120000;

    private static final long EXPIRY_NEVER = -1l;

    private static Log logger = LogFactory.getLog(Element.class);

    private static final long serialVersionUID = -935757449385127201L;

    private final long creationTime;

    private final Serializable key;

    private final long timeToLive;

    private Serializable value;

    /**
     * Constructor. Entries created with this constructor never expire.
     *
     * <p>
     * The key and value stored in this element are copies of the ones passed as
     * arguments.
     * </p>
     *
     * @param newKey
     *          the new key for this entry
     * @param newValue
     *          the new value for this entry
     * @throws ObjectCannotBeCopiedException
     *           if the key or the value cannot be copied using serialization
     */
    public Element(Serializable newKey, Serializable newValue) throws ObjectCannotBeCopiedException {
        this(newKey, newValue, EXPIRY_NEVER);
    }

    /**
     * Constructor.
     *
     * <p>
     * The key and value stored in this element are copies of the ones passed as
     * arguments.
     * </p>
     *
     * @param newKey
     *          the new key for this entry
     * @param newValue
     *          the new value for this entry
     * @param newTimeToLive
     *          the number of milliseconds until the cache entry will expire
     * @throws ObjectCannotBeCopiedException
     *           if the key or the value cannot be copied using serialization
     */
    public Element(Serializable newKey, Serializable newValue, long newTimeToLive)
            throws ObjectCannotBeCopiedException {
        this(newKey, newValue, System.currentTimeMillis(), newTimeToLive);
    }

    private Element(Serializable newKey, Serializable newValue, long newCreationTime, long newTimeToLive)
            throws ObjectCannotBeCopiedException {
        super();
        key = copy(newKey);

        setValue(newValue);
        creationTime = newCreationTime;

        boolean invalidTimeToLive = newTimeToLive <= 0 && newTimeToLive != EXPIRY_NEVER;
        timeToLive = invalidTimeToLive ? DEFAULT_TIME_TO_LIVE_MS : newTimeToLive;
    }

    /**
     * @see Object#clone()
     */
    public Object clone() {
        Element newElement = new Element(key, value, creationTime, timeToLive);
        return newElement;
    }

    /**
     * @see Object#equals(Object)
     */
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (!(obj instanceof Element)) {
            return false;
        }
        Element other = (Element) obj;
        if (!ObjectUtils.nullSafeEquals(key, other.key)) {
            return false;
        }
        if (!ObjectUtils.nullSafeEquals(value, other.value)) {
            return false;
        }
        return true;
    }

    /**
     * @return the creation time (in milliseconds) of this cache element
     */
    public final long getCreationTime() {
        return creationTime;
    }

    /**
     * @return the key of this cache element
     */
    public final Serializable getKey() {
        return key;
    }

    /**
     * @return the number of milliseconds until the cache entry will expire
     */
    public final long getTimeToLive() {
        return timeToLive;
    }

    /**
     * @return the value of this cache element
     */
    public final Serializable getValue() {
        return value;
    }

    /**
     * @see Object#hashCode()
     */
    public int hashCode() {
        int multiplier = 31;
        int hash = 7;
        hash = multiplier * hash + key.hashCode();
        hash = multiplier * hash + Objects.nullSafeHashCode(value);
        return hash;
    }

    /**
     * @return <code>true</code> if this cache element has not expired yet. This
     *         method always returns <code>true</code> for eternal entries.
     * @see #Element(Serializable, Serializable)
     */
    public final boolean isAlive() {
        if (timeToLive == EXPIRY_NEVER) {
            return true;
        }

        long currentTime = System.currentTimeMillis();
        long delta = currentTime - creationTime;

        return delta < timeToLive;
    }

    /**
     * @return <code>true</code> if this cache element has expired
     * @see #isAlive()
     */
    public final boolean isExpired() {
        return !isAlive();
    }

    /**
     * Sets the value for this cache element.
     *
     * @param newValue
     *          the new value for this cache element
     * @throws ObjectCannotBeCopiedException
     *           if the key or the value cannot be copied using serialization
     */
    public final void setValue(Serializable newValue) throws ObjectCannotBeCopiedException {
        value = copy(newValue);
    }

    /**
     * @see Object#toString()
     */
    public String toString() {
        return Objects.identityToString(this).append("[key=").append(StringUtils.quoteIfString(key)).append(", ")
                .append("value=").append(StringUtils.quoteIfString(value)).append(", ").append("creationTime=")
                .append(new Date(creationTime)).append(", ").append("timeToLive=").append(timeToLive).append("]")
                .toString();
    }

    private void close(InputStream closeable) {
        if (closeable == null) {
            return;
        }

        try {
            closeable.close();
        } catch (Exception exception) {
            String clazz = closeable.getClass().getName();
            logger.error("Unable to close " + clazz, exception);
        }
    }

    private void close(OutputStream closeable) {
        if (closeable == null) {
            return;
        }

        try {
            closeable.close();
        } catch (Exception exception) {
            String clazz = closeable.getClass().getName();
            logger.error("Unable to close " + clazz, exception);
        }
    }

    private Serializable copy(Serializable oldValue) throws ObjectCannotBeCopiedException {
        Serializable newValue = null;

        ByteArrayInputStream oldValueInputStream = null;
        ByteArrayOutputStream oldValueOutputStream = new ByteArrayOutputStream();

        ObjectInputStream newValueInputStream = null;
        ObjectOutputStream newValueOutputStream = null;

        try {
            newValueOutputStream = new ObjectOutputStream(oldValueOutputStream);
            newValueOutputStream.writeObject(oldValue);

            byte[] oldValueAsByteArray = oldValueOutputStream.toByteArray();
            oldValueInputStream = new ByteArrayInputStream(oldValueAsByteArray);

            newValueInputStream = new ObjectInputStream(oldValueInputStream);
            newValue = (Serializable) newValueInputStream.readObject();

        } catch (Exception exception) {
            String errMsg = "Unable to copy value " + oldValue;
            throw new ObjectCannotBeCopiedException(errMsg, exception);

        } finally {
            close(newValueInputStream);
            close(newValueOutputStream);
            close(oldValueInputStream);
            close(oldValueOutputStream);
        }
        return newValue;
    }
}