/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.server.distcache; import com.caucho.distcache.ExtCacheEntry; import com.caucho.util.Alarm; import com.caucho.util.HashKey; import com.caucho.util.Hex; import java.lang.ref.SoftReference; import java.sql.Blob; /** * An entry in the cache map */ public final class MnodeEntry extends MnodeValue implements ExtCacheEntry { public static final MnodeEntry NULL = new MnodeEntry(null, 0, 0, null, null, 0, 0, 0, 0, 0, 0, false, true); public static final HashKey NULL_KEY = new HashKey(new byte[32]); public static final HashKey ANY_KEY = createAnyKey(32); private final long _leaseTimeout; private final boolean _isServerVersionValid; private final boolean _isImplicitNull; private final long _lastModifiedTime; private volatile long _lastAccessTime; private int _leaseOwner = -1; private long _leaseExpireTime; private long _lastRemoteAccessTime; private int _hits = 0; private SoftReference<Object> _valueRef; private transient Blob _blob; public MnodeEntry(HashKey valueHash, long valueLength, long version, Object value, HashKey cacheHash, long flags, long expireTimeout, long idleTimeout, long leaseTimeout, long lastAccessTime, long lastUpdateTime, boolean isServerVersionValid, boolean isImplicitNull) { super(HashKey.getHash(valueHash), valueLength, version, HashKey.getHash(cacheHash), flags, expireTimeout, idleTimeout); _leaseTimeout = leaseTimeout; _lastRemoteAccessTime = lastAccessTime; _lastModifiedTime = lastUpdateTime; _lastAccessTime = Alarm.getExactTime(); _isImplicitNull = isImplicitNull; _isServerVersionValid = isServerVersionValid; if (value != null) _valueRef = new SoftReference<Object>(value); } public MnodeEntry(MnodeValue mnodeValue, Object value, long leaseTimeout, long lastAccessTime, long lastUpdateTime, boolean isServerVersionValid, boolean isImplicitNull) { super(mnodeValue); _leaseTimeout = leaseTimeout; _lastRemoteAccessTime = lastAccessTime; _lastModifiedTime = lastUpdateTime; _lastAccessTime = Alarm.getExactTime(); _isImplicitNull = isImplicitNull; _isServerVersionValid = isServerVersionValid; if (value != null) _valueRef = new SoftReference<Object>(value); } public MnodeEntry(MnodeEntry oldMnodeValue, long idleTimeout, long lastUpdateTime) { super(oldMnodeValue.getValueHash(), oldMnodeValue.getValueLength(), oldMnodeValue.getVersion(), oldMnodeValue.getCacheHash(), oldMnodeValue.getFlags(), oldMnodeValue.getModifiedExpireTimeout(), idleTimeout); _leaseTimeout = oldMnodeValue.getLeaseTimeout(); _lastRemoteAccessTime = lastUpdateTime; _lastModifiedTime = lastUpdateTime; _lastAccessTime = Alarm.getExactTime(); _leaseExpireTime = oldMnodeValue._leaseExpireTime; _leaseOwner = oldMnodeValue._leaseOwner; _isImplicitNull = oldMnodeValue.isImplicitNull(); _isServerVersionValid = oldMnodeValue.isServerVersionValid(); Object value = oldMnodeValue.getValue(); if (value != null) _valueRef = new SoftReference<Object>(value); } @Override public HashKey getKeyHash() { throw new UnsupportedOperationException(getClass().getName()); } /** * Returns the last access time. */ public long getLastAccessedTime() { return _lastAccessTime; } /** * Sets the last access time. */ public void setLastAccessTime(long accessTime) { _lastAccessTime = accessTime; } /** * Returns the last remote access time. */ public long getLastRemoteAccessTime() { return _lastRemoteAccessTime; } /** * Sets the last remote access time. */ public void setLastRemoteAccessTime(long accessTime) { _lastRemoteAccessTime = accessTime; } /** * Returns the last update time. */ @Override public long getLastModifiedTime() { return _lastModifiedTime; } /** * Returns the expiration time */ public final long getExpirationTime() { return _lastModifiedTime + getModifiedExpireTimeout(); } public final boolean isLocalExpired(int serverIndex, long now, long localExpireTimeout) { if (! _isServerVersionValid) return true; else if (now <= _lastAccessTime + localExpireTimeout) return false; else if (_leaseOwner == serverIndex && now <= _leaseExpireTime) return false; else return true; } public final boolean isLeaseExpired(long now) { return _leaseExpireTime <= now; } /** * Returns true if the local (unchecked) expire time. */ public final boolean isLocalExpired(long now, CacheConfig config) { if (isExpired(now)) return true; else if (_lastAccessTime + config.getLocalExpireTimeout() <= now) return true; else return false; } /** * Returns true is the entry has expired for being idle or having * expired. */ public final boolean isExpired(long now) { return isIdleExpired(now) || isValueExpired(now); } /** * Returns true if the value of the entry has expired. */ public final boolean isValueExpired(long now) { return _lastModifiedTime + getModifiedExpireTimeout() < now; } /** * Returns true is the entry has remained idle too long. */ public final boolean isIdleExpired(long now) { return _lastAccessTime + getAccessedExpireTimeout() < now; } /** * Returns the lease owner */ @Override public final int getLeaseOwner() { return _leaseOwner; } /** * Sets the owner */ public final void setLeaseOwner(int leaseOwner, long now) { if (leaseOwner > 2) { _leaseOwner = leaseOwner; _leaseExpireTime = now + _leaseTimeout; } else { _leaseOwner = -1; _leaseExpireTime = 0; } } /** * Sets the owner */ public final void clearLease() { _leaseOwner = -1; _leaseExpireTime = 0; } /** * Returns the idle window to avoid too many updates */ public long getAccessExpireTimeoutWindow() { long window = getAccessedExpireTimeout() / 4; long windowMax = 15 * 60 * 1000L; if (window < windowMax) return window; else return windowMax; } /** * Returns the timeout for a lease of the cache entry */ @Override public long getLeaseTimeout() { return _leaseTimeout; } /** * Sets the deserialized value for the entry. */ public final void setObjectValue(Object value) { if (value != null && (_valueRef == null || _valueRef.get() == null)) _valueRef = new SoftReference<Object>(value); } /** * Returns true if the value is null */ @Override public boolean isValueNull() { return getValueHash() == null; } /** * Returns the deserialized value for the entry. */ @Override public final Object getValue() { SoftReference<Object> valueRef = _valueRef; if (valueRef != null) { _hits++; return valueRef.get(); } else return null; } public Blob getBlob() { return _blob; } public void setBlob(Blob blob) { _blob = blob; } @Override public HashKey getValueHashKey() { return HashKey.create(getValueHash()); } public HashKey getCacheHashKey() { return HashKey.create(getCacheHash()); } /** * Returns true if the server version (startup count) matches * the database. */ public boolean isServerVersionValid() { return _isServerVersionValid; } /** * If the null value is due to a missing item in the database. */ public boolean isImplicitNull() { return _isImplicitNull; } public boolean isUnloadedValue() { return this == NULL; } /** * Compares values */ public int compareTo(MnodeEntry mnode) { if (getVersion() < mnode.getVersion()) return -1; else if (mnode.getVersion() < getVersion()) return 1; else if (getValueHashKey() == null) return -1; else return getValueHashKey().compareTo(mnode.getValueHashKey()); } // // statistics // @Override public int getLoadCount() { return 0; } // // jcache stubs // /** * Implements a method required by the interface that should never be * called> */ @Override public Object getKey() { throw new UnsupportedOperationException(getClass().getName()); } /** * Implements a method required by the interface that should never be * called> */ public Object setValue(Object value) { throw new UnsupportedOperationException(getClass().getName()); } public long getCreationTime() { throw new UnsupportedOperationException(getClass().getName()); } @Override public boolean isValid() { return (! isExpired(Alarm.getCurrentTime())); } /* @Override public long getCost() { throw new UnsupportedOperationException(getClass().getName()); } */ public int getHits() { return _hits; } private static HashKey createAnyKey(int len) { byte []value = new byte[len]; for (int i = 0; i < len; i++) { value[i] = (byte) 0xff; } return new HashKey(value); } @Override public String toString() { return (getClass().getSimpleName() + "[value=" + Hex.toHex(getValueHash(), 0, 4) + ",flags=0x" + Long.toHexString(getFlags()) + ",version=" + getVersion() + ",lease=" + _leaseOwner + "]"); } }