/FileExtraction/Java_unseen/dlitz_resin/modules/resin/src/com/caucho/server/distcache/DistCacheEntry.java
/*
 * 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 java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import com.caucho.cloud.topology.TriadOwner;
import com.caucho.distcache.ExtCacheEntry;
import com.caucho.util.Alarm;
import com.caucho.util.HashKey;
import com.caucho.util.Hex;

/**
 * An entry in the cache map
 */
public class DistCacheEntry implements ExtCacheEntry {
  private final CacheStoreManager _cacheService;
  private final HashKey _keyHash;

  private final TriadOwner _owner;

  private Object _key;

  private final AtomicBoolean _isReadUpdate = new AtomicBoolean();

  private final AtomicReference<MnodeEntry> _mnodeEntry
    = new AtomicReference<MnodeEntry>(MnodeEntry.NULL);
  
  private final AtomicInteger _loadCount = new AtomicInteger();

  public DistCacheEntry(CacheStoreManager engine,
                        Object key,
                        HashKey keyHash,
                        TriadOwner owner)
  {
    _cacheService = engine;
    _key = key;
    _keyHash = keyHash;
    _owner = owner;
  }

  public DistCacheEntry(CacheStoreManager engine,
                        Object key,
                        HashKey keyHash,
                        TriadOwner owner,
                        CacheConfig config)
  {
    _cacheService = engine;
    _key = key;
    _keyHash = keyHash;
    _owner = owner;
  }

  /**
   * Returns the key for this entry in the Cache.
   */
  @Override
  public final Object getKey()
  {
    return _key;
  }

  /**
   * Returns the keyHash
   */
  @Override
  public final HashKey getKeyHash()
  {
    return _keyHash;
  }

  /**
   * Returns the value of the cache entry.
   */
  @Override
  public Object getValue()
  {
    return getMnodeEntry().getValue();
  }

  /**
   * Returns true if the value is null.
   */
  @Override
  public boolean isValueNull()
  {
    return getMnodeEntry().isValueNull();
  }

  /**
   * Returns the cacheHash
   */
  public final HashKey getCacheHash()
  {
    return getMnodeEntry().getCacheHashKey();
  }
  
  public final int getUserFlags()
  {
    return getMnodeEntry().getUserFlags();
  }

  /**
   * Returns the owner
   */
  public final TriadOwner getOwner()
  {
    return _owner;
  }

  /**
   * Returns the value section of the entry.
   */
  public final MnodeEntry getMnodeEntry()
  {
    return _mnodeEntry.get();
  }

  /**
   * Peeks the current value without checking the backing store.
   */
  public Object peek()
  {
    MnodeEntry entry = getMnodeEntry();
    
    if (entry != null)
      return entry.getValue();
    else
      return null;
  }

  /**
   * Returns the object for the given key, checking the backing if necessary.
   * If it is not found, the optional cacheLoader is invoked, if present.
   */
  public Object get(CacheConfig config)
  {
    long now = Alarm.getCurrentTime();

    return _cacheService.get(this, config, now);
  }

  /**
   * Returns the object for the given key, checking the backing if necessary
   */
  public MnodeEntry getMnodeValue(CacheConfig config)
  {
    long now = Alarm.getCurrentTime();

    return _cacheService.getMnodeValue(this, config, now); // , false);
  }

  /**
   * Fills the value with a stream
   */
  public boolean getStream(OutputStream os, CacheConfig config)
    throws IOException
  {
    return _cacheService.getStream(this, os, config);
  }

  public HashKey getValueHash(Object value, CacheConfig config)
  {
    return _cacheService.getValueHash(value, config).getValue();
  }
 
  /**
   * Sets the current value
   */
  public void put(Object value, CacheConfig config)
  {
    _cacheService.put(this, value, config);
  }

  /**
   * Sets the value by an input stream
   */
  public ExtCacheEntry put(InputStream is,
                           CacheConfig config,
                           long accessedExpireTimeout,
                           long modifiedExpireTimeout)
    throws IOException
  {
    return _cacheService.putStream(this, is, config, 
                                   accessedExpireTimeout,
                                   modifiedExpireTimeout, 
                                   0);
  }

  /**
   * Sets the value by an input stream
   */
  public ExtCacheEntry put(InputStream is,
                           CacheConfig config,
                           long accessedExpireTimeout,
                           long modifiedExpireTimeout,
                           int flags)
    throws IOException
  {
    return _cacheService.putStream(this, is, config, 
                                   accessedExpireTimeout, 
                                   modifiedExpireTimeout,
                                   flags);
  }

  /**
   * Sets the current value
   */
  public Object getAndPut(Object value, CacheConfig config)
  {
    return _cacheService.getAndPut(this, value, config);
  }

  /**
   * Sets the current value
   */
  public HashKey compareAndPut(HashKey testValue, 
                               Object value, 
                               CacheConfig config)
  {
    return _cacheService.compareAndPut(this, testValue, value, config);
  }

  /**
   * Sets the current value
   */
  public boolean compareAndPut(long version,
                               HashKey value,
                               long valueLength,
                               CacheConfig config)
  {
    return _cacheService.compareAndPut(this, version, value, valueLength, config);
  }

  /**
   * Remove the value
   */
  public boolean remove(CacheConfig config)
  {
    return _cacheService.remove(this, config);
  }

  /**
   * Conditionally starts an update of a cache item, allowing only a
   * single thread to update the data.
   *
   * @return true if the thread is allowed to update
   */
  public final boolean startReadUpdate()
  {
    return _isReadUpdate.compareAndSet(false, true);
  }

  /**
   * Completes an update of a cache item.
   */
  public final void finishReadUpdate()
  {
    _isReadUpdate.set(false);
  }

  /**
   * Sets the current value.
   */
  public final boolean compareAndSet(MnodeEntry oldMnodeValue,
                                     MnodeEntry mnodeValue)
  {
    return _mnodeEntry.compareAndSet(oldMnodeValue, mnodeValue);
  }

  @Override
  public HashKey getValueHashKey()
  {
    MnodeEntry entry = getMnodeEntry();
    
    if (entry != null)
      return entry.getValueHashKey();
    else
      return null;
  }

  public byte []getValueHashArray()
  {
    return getMnodeEntry().getValueHash();
  }

  @Override
  public long getValueLength()
  {
    return getMnodeEntry().getValueLength();
  }

  @Override
  public long getAccessedExpireTimeout()
  {
    return getMnodeEntry().getAccessedExpireTimeout();
  }

  @Override
  public long getModifiedExpireTimeout()
  {
    return getMnodeEntry().getModifiedExpireTimeout();
  }
  
  @Override
  public boolean isExpired(long now)
  {
    MnodeEntry entry = getMnodeEntry();
    
    if (entry != null)
      return entry.isExpired(now);
    else
      return false;
  }

  @Override
  public long getLeaseTimeout()
  {
    return getMnodeEntry().getLeaseTimeout();
  }

  @Override
  public int getLeaseOwner()
  {
    return getMnodeEntry().getLeaseOwner();
  }

  public void clearLease()
  {
    MnodeEntry mnodeValue = getMnodeEntry();

    if (mnodeValue != null)
      mnodeValue.clearLease();
  }

  public long getCost()
  {
    return 0;
  }

  public long getCreationTime()
  {
    return getMnodeEntry().getCreationTime();
  }

  public long getExpirationTime()
  {
    return getMnodeEntry().getExpirationTime();
  }

  public int getHits()
  {
    return getMnodeEntry().getHits();
  }

  public long getLastAccessedTime()
  {
    return getMnodeEntry().getLastAccessedTime();
  }

  public long getLastModifiedTime()
  {
    return getMnodeEntry().getLastModifiedTime();
  }

  public long getVersion()
  {
    MnodeEntry entry = getMnodeEntry();
    
    if (entry != null)
      return entry.getVersion();
    else
      return 0;
  }

  public boolean isValid()
  {
    return getMnodeEntry().isValid();
  }


  public Object setValue(Object value)
  {
    return getMnodeEntry().setValue(value);
  }
  
  //
  // statistics
  //
  
  public void addLoadCount()
  {
    _loadCount.incrementAndGet();
  }
  
  @Override
  public int getLoadCount()
  {
    return _loadCount.get();
  }

  @Override
  public String toString()
  {
    return (getClass().getSimpleName()
            + "[key=" + _key
            + ",keyHash=" + Hex.toHex(_keyHash.getHash(), 0, 4)
            + ",owner=" + _owner
            + "]");
  }
}
