/**
   * Called immediately after an element has been put into the cache and the element already existed
   * in the cache. This is thus an update.
   *
   * <p>The {@link net.sf.ehcache.Cache#put(net.sf.ehcache.Element)} method will block until this
   * method returns.
   *
   * <p>Implementers may wish to have access to the Element's fields, including value, so the
   * element is provided. Implementers should be careful not to modify the element. The effect of
   * any modifications is undefined.
   *
   * @param cache the cache emitting the notification
   * @param element the element which was just put into the cache.
   */
  public final void notifyElementUpdated(final Ehcache cache, final Element element)
      throws CacheException {
    if (notAlive()) {
      return;
    }
    if (!replicateUpdates) {
      return;
    }

    if (replicateUpdatesViaCopy) {
      if (!element.isSerializable()) {
        if (LOG.isWarnEnabled()) {
          LOG.warn(
              "Object with key "
                  + element.getObjectKey()
                  + " is not Serializable and cannot be updated via copy.");
        }
        return;
      }
      addToReplicationQueue(new CacheEventMessage(EventMessage.PUT, cache, element, null));
    } else {
      if (!element.isKeySerializable()) {
        if (LOG.isWarnEnabled()) {
          LOG.warn(
              "Object with key "
                  + element.getObjectKey()
                  + " does not have a Serializable key and cannot be replicated via invalidate.");
        }
        return;
      }
      addToReplicationQueue(
          new CacheEventMessage(EventMessage.REMOVE, cache, null, element.getKey()));
    }
  }
예제 #2
0
  /**
   * Both CacheEntryFactory can return an Element rather than just a regular value this method test
   * this, making a fresh Element otherwise. It also enforces the rule that the CacheEntryFactory
   * must provide the same key (via equals() not necessarily same object) if it is returning an
   * Element.
   *
   * @param key
   * @param value
   * @return the Element to be put back in the cache
   * @throws CacheException for various illegal states which could be harmful
   */
  protected static Element makeAndCheckElement(Object key, Object value) throws CacheException {
    // check if null
    if (value == null) {
      return new Element(key, value);
    }

    // simply build a new element using the supplied key
    if (!(value instanceof Element)) {
      return new Element(key, value);
    }

    // It is already an element - perform sanity checks
    Element element = (Element) value;
    if ((element.getObjectKey() == null) && (key == null)) {
      return element;
    } else if (element.getObjectKey() == null) {
      throw new CacheException("CacheEntryFactory returned an Element with a null key");
    } else if (!element.getObjectKey().equals(key)) {
      throw new CacheException(
          "CacheEntryFactory returned an Element with a different key: "
              + element.getObjectKey()
              + " compared to the key that was requested: "
              + key);
    } else {
      return element;
    }
  }
예제 #3
0
  /**
   * Puts an element into the disk store.
   *
   * <p>This method is not synchronized. It is however threadsafe. It uses fine-grained
   * synchronization on the spool.
   */
  public final void put(final Element element) {
    try {
      checkActive();

      // Spool the element
      if (spoolAndExpiryThread.isAlive()) {
        synchronized (spoolLock) {
          spool.put(element.getObjectKey(), element);
        }
      } else {
        LOG.error(
            name
                + "Cache: Elements cannot be written to disk store because the"
                + " spool thread has died.");
        synchronized (spoolLock) {
          spool.clear();
        }
      }

    } catch (Exception e) {
      LOG.error(
          name
              + "Cache: Could not write disk store element for "
              + element.getObjectKey()
              + ". Initial cause was "
              + e.getMessage(),
          e);
    }
  }
예제 #4
0
  /** Equals comparison with another element, based on the key. */
  public final boolean equals(Object object) {
    if (object == null || !(object instanceof Element)) {
      return false;
    }

    Element element = (Element) object;
    if (key == null || element.getObjectKey() == null) {
      return false;
    }

    return key.equals(element.getObjectKey());
  }
예제 #5
0
  /** {@inheritDoc} */
  public Element putIfAbsent(Element element) throws NullPointerException {
    LOG.debug("cache {} putIfAbsent {}", cache.getName(), element);
    XATransactionContext context = getOrCreateTransactionContext();
    Element previous = getCurrentElement(element.getObjectKey(), context);

    if (previous == null) {
      Element oldElement = getQuietFromUnderlyingStore(element.getObjectKey());
      Element elementForWrite = copyElementForWrite(element);
      context.addCommand(new StorePutCommand(oldElement, elementForWrite), elementForWrite);
    }

    return copyElementForRead(previous);
  }
예제 #6
0
  /** {@inheritDoc} */
  public Element removeElement(Element element, ElementValueComparator comparator)
      throws NullPointerException {
    LOG.debug("cache {} removeElement {}", cache.getName(), element);
    XATransactionContext context = getOrCreateTransactionContext();
    Element previous = getCurrentElement(element.getKey(), context);

    Element elementForWrite = copyElementForWrite(element);
    if (previous != null && comparator.equals(previous, elementForWrite)) {
      Element oldElement = getQuietFromUnderlyingStore(element.getObjectKey());
      context.addCommand(
          new StoreRemoveCommand(element.getObjectKey(), oldElement), elementForWrite);
      return copyElementForRead(previous);
    }
    return null;
  }
  /**
   * Tests expiry notification is hooked up to searchInDiskStore. This test is not exact, because
   * the expiry thread may also expire some.
   *
   * @throws InterruptedException
   * @throws CacheException
   */
  @Test
  public void testExpiryViaDiskStoreCheckingOnGet()
      throws InterruptedException, CacheException, IOException {
    // Overflow 10 elements to disk store
    for (int i = 0; i < 20; i++) {
      Element element = new Element("" + i, new Date());
      cache.put(element);
    }

    // Wait for expiry
    Thread.sleep(5999);

    // Trigger expiry
    for (int i = 0; i < 20; i++) {
      cache.get("" + i);
    }

    CountingCacheEventListener listener = getCountingCacheEventListener(cache);
    List<CacheEvent> notifications = listener.getCacheElementsExpired();
    for (int i = 0; i < notifications.size(); i++) {
      Element element = notifications.get(i).getElement();
      element.getObjectKey();
    }
    assertThat(notifications, hasSize(greaterThanOrEqualTo(10)));
  }
예제 #8
0
  /**
   * Refresh a single element.
   *
   * @param element the Element to refresh
   * @param backingCache the underlying {@link Ehcache}.
   * @param quiet whether to use putQuiet or not, if true replication will not occur
   * @return the refreshed Element
   * @throws Exception
   * @since 1.6.1
   */
  protected Element refreshElement(final Element element, Ehcache backingCache, boolean quiet)
      throws Exception {
    Object key = element.getObjectKey();

    if (LOG.isLoggable(Level.FINE)) {
      LOG.fine(getName() + ": refreshing element with key " + key);
    }

    final Element replacementElement;

    if (factory instanceof UpdatingCacheEntryFactory) {
      // update the value of the cloned Element in place
      replacementElement = element;
      ((UpdatingCacheEntryFactory) factory)
          .updateEntryValue(key, replacementElement.getObjectValue());

      // put the updated element back into the backingCache, without updating stats
      // It is not usually necessary to do this. We do this in case the element expired
      // or idles out of the backingCache. In that case we hold a reference to it but the
      // backingCache no longer does.
    } else {
      final Object value = factory.createEntry(key);
      replacementElement = makeAndCheckElement(key, value);
    }

    if (quiet) {
      backingCache.putQuiet(replacementElement);
    } else {
      backingCache.put(replacementElement);
    }
    return replacementElement;
  }
예제 #9
0
  /**
   * Removes expired elements.
   *
   * <p>Note that the DiskStore cannot efficiently expire based on TTI. It does it on TTL. However
   * any gets out of the DiskStore are check for both before return.
   *
   * @noinspection SynchronizeOnNonFinalField
   */
  public void expireElements() {
    final long now = System.currentTimeMillis();

    // Clean up the spool
    synchronized (spoolLock) {
      for (Iterator iterator = spool.values().iterator(); iterator.hasNext(); ) {
        final Element element = (Element) iterator.next();
        if (element.isExpired()) {
          // An expired element
          if (LOG.isDebugEnabled()) {
            LOG.debug(name + "Cache: Removing expired spool element " + element.getObjectKey());
          }
          iterator.remove();
          notifyExpiryListeners(element);
        }
      }
    }

    Element element = null;
    RegisteredEventListeners listeners = cache.getCacheEventNotificationService();
    synchronized (diskElements) {
      // Clean up disk elements
      for (Iterator iterator = diskElements.entrySet().iterator(); iterator.hasNext(); ) {
        final Map.Entry entry = (Map.Entry) iterator.next();
        final DiskElement diskElement = (DiskElement) entry.getValue();

        if (now >= diskElement.expiryTime) {
          // An expired element
          if (LOG.isDebugEnabled()) {
            LOG.debug(
                name
                    + "Cache: Removing expired spool element "
                    + entry.getKey()
                    + " from Disk Store");
          }

          iterator.remove();

          // only load the element from the file if there is a listener interested in hearing about
          // its expiration
          if (listeners.hasCacheEventListeners()) {
            try {
              element = loadElementFromDiskElement(diskElement);
              notifyExpiryListeners(element);
            } catch (Exception exception) {
              LOG.error(
                  name
                      + "Cache: Could not remove disk store entry for "
                      + entry.getKey()
                      + ". Error was "
                      + exception.getMessage(),
                  exception);
            }
          }
          freeBlock(diskElement);
        }
      }
    }
  }
예제 #10
0
  /** {@inheritDoc} */
  public boolean put(Element element) throws CacheException {
    LOG.debug("cache {} put {}", cache.getName(), element);
    // this forces enlistment so the XA transaction timeout can be propagated to the XA resource
    getOrCreateTransactionContext();

    Element oldElement = getQuietFromUnderlyingStore(element.getObjectKey());
    return internalPut(new StorePutCommand(oldElement, copyElementForWrite(element)));
  }
예제 #11
0
  @Test
  public void testLFUEvictionFromDiskStore() throws IOException, InterruptedException {
    Cache cache =
        new Cache(
            "testNonPersistent",
            1,
            MemoryStoreEvictionPolicy.LFU,
            true,
            null,
            false,
            2000,
            1000,
            false,
            1,
            null,
            null,
            10);
    manager.addCache(cache);
    Store store = cache.getStore();

    Element element;

    assertEquals(0, store.getSize());

    for (int i = 0; i < 11; i++) {
      element = new Element("key" + i, "value" + i);
      cache.put(element);
      if (i > 0) {
        cache.get("key" + i);
        cache.get("key" + i);
        cache.get("key" + i);
        cache.get("key" + i);
      }
    }

    // allow to move through spool
    Thread.sleep(220);
    assertEquals(10, store.getOnDiskSize());

    element = new Element("keyNew", "valueNew");
    store.put(element);

    // allow to get out of spool
    Thread.sleep(220);
    assertEquals(10, store.getOnDiskSize());
    // check new element not evicted
    assertNotNull(store.get(element.getObjectKey()));
    // check evicted honours LFU policy
    assertNull(store.get("key0"));

    for (int i = 0; i < 2000; i++) {
      store.put(new Element("" + i, new Date()));
    }
    // wait for spool to empty
    waitLonger();

    assertEquals(10, store.getOnDiskSize());
  }
예제 #12
0
 /**
  * Saves the key to our fast access AtomicReferenceArray
  *
  * @param elementJustAdded the new element
  */
 protected void saveKey(Element elementJustAdded) {
   int index = incrementIndex();
   Object key = keyArray.get(index);
   Element oldElement = null;
   if (key != null) {
     oldElement = (Element) map.get(key);
   }
   if (oldElement != null) {
     if (policy.compare(oldElement, elementJustAdded)) {
       // new one will always be more desirable for eviction as no gets yet, unless no gets on old
       // one.
       // Consequence of this algorithm
       keyArray.set(index, elementJustAdded.getObjectKey());
     }
   } else {
     keyArray.set(index, elementJustAdded.getObjectKey());
   }
 }
예제 #13
0
 private void evictIfRequired(final K key, final V value) {
   if (maxEntriesLocalHeap == 0) {
     return;
   }
   int evictions = MAX_EVICTIONS;
   while (maxEntriesLocalHeap < mappingCount() && evictions-- > 0) {
     final Element evictionCandidate = findEvictionCandidate(key, value);
     if (evictionCandidate != null) {
       remove(evictionCandidate.getObjectKey(), evictionCandidate, callback);
     }
   }
 }
예제 #14
0
 private void writeOrReplaceEntry(Object object) throws IOException {
   Element element = (Element) object;
   if (element == null) {
     return;
   }
   final Serializable key = (Serializable) element.getObjectKey();
   removeOldEntryIfAny(key);
   if (maxElementsOnDisk > 0 && diskElements.size() >= maxElementsOnDisk) {
     evictLfuDiskElement();
   }
   writeElement(element, key);
 }
예제 #15
0
  /**
   * Removes the element chosen by the eviction policy
   *
   * @param elementJustAdded it is possible for this to be null
   */
  protected void removeElementChosenByEvictionPolicy(Element elementJustAdded) {

    if (LOG.isLoggable(Level.FINEST)) {
      LOG.finest("Cache is full. Removing element ...");
    }

    Element element = findEvictionCandidate(elementJustAdded);
    // this CAN happen rarely. Let the store get one bigger
    if (element == null) {
      LOG.info("Eviction selection miss. Selected element is null");
      return;
    }

    // If the element is expired remove
    if (element.isExpired()) {
      remove(element.getObjectKey());
      notifyExpiry(element);
      return;
    }

    evict(element);
    remove(element.getObjectKey());
  }
예제 #16
0
  /** {@inheritDoc} */
  public boolean replace(Element old, Element element, ElementValueComparator comparator)
      throws NullPointerException, IllegalArgumentException {
    LOG.debug("cache {} replace2 {}", cache.getName(), element);
    XATransactionContext context = getOrCreateTransactionContext();
    Element previous = getCurrentElement(element.getKey(), context);

    boolean replaced = false;
    if (previous != null && comparator.equals(previous, copyElementForWrite(old))) {
      Element oldElement = getQuietFromUnderlyingStore(element.getObjectKey());
      Element elementForWrite = copyElementForWrite(element);
      context.addCommand(new StorePutCommand(oldElement, elementForWrite), elementForWrite);
      replaced = true;
    }
    return replaced;
  }
예제 #17
0
 /**
  * Create a new write operation for a particular element and creation time
  *
  * @param element the element to write
  * @param creationTime the creation time of the operation
  */
 public WriteOperation(Element element, long creationTime) {
   this.element =
       new Element(
           element.getObjectKey(),
           element.getObjectValue(),
           element.getVersion(),
           element.getCreationTime(),
           element.getLastAccessTime(),
           element.getHitCount(),
           false,
           element.getTimeToLive(),
           element.getTimeToIdle(),
           element.getLastUpdateTime());
   this.creationTime = creationTime;
 }
  /**
   * Called immediately after an element has been removed. The remove method will block until this
   * method returns.
   *
   * <p>This implementation queues the removal notification for in order replication to peers.
   *
   * @param cache the cache emitting the notification
   * @param element just deleted
   */
  public final void notifyElementRemoved(final Ehcache cache, final Element element)
      throws CacheException {
    if (!replicateRemovals) {
      return;
    }

    if (!element.isKeySerializable()) {
      if (LOG.isWarnEnabled()) {
        LOG.warn(
            "Key " + element.getObjectKey() + " is not Serializable and cannot be replicated.");
      }
      return;
    }
    addToReplicationQueue(
        new CacheEventMessage(EventMessage.REMOVE, cache, null, element.getKey()));
  }
  @Override
  public void after(Object target, Object result, Throwable throwable, Object[] args) {
    if (isDebug) {
      logger.afterInterceptor(target, args, result, throwable);
    }

    try {
      ((CacheNameAccessor) target)._$PINPOINT$_setCacheName(DEFAULT_FRONTCACHE_NAME);

      if (args[0] instanceof Element) {
        Element element = (Element) args[0];
        ((CacheKeyAccessor) target)._$PINPOINT$_setCacheKey(element.getObjectKey());
      }
    } catch (Exception e) {
      logger.error("failed to add metadata: {}", e);
    }
  }
예제 #20
0
 private void notifyEvictionListeners(DiskElement diskElement) {
   RegisteredEventListeners listeners = cache.getCacheEventNotificationService();
   // only load the element from the file if there is a listener interested in hearing about its
   // expiration
   if (listeners.hasCacheEventListeners()) {
     Element element = null;
     try {
       element = loadElementFromDiskElement(diskElement);
       cache.getCacheEventNotificationService().notifyElementEvicted(element, false);
     } catch (Exception exception) {
       LOG.error(
           name
               + "Cache: Could not notify disk store eviction of "
               + element.getObjectKey()
               + ". Error was "
               + exception.getMessage(),
           exception);
     }
   }
 }
예제 #21
0
 /**
  * Spools all elements to disk, in preparation for shutdown.
  *
  * <p>Relies on being called from a synchronized method
  *
  * <p>This revised implementation is a little slower but avoids using increased memory during the
  * method.
  */
 protected final void spoolAllToDisk() {
   Object[] keys = getKeyArray();
   for (int i = 0; i < keys.length; i++) {
     Element element = (Element) map.get(keys[i]);
     if (element != null) {
       if (!element.isSerializable()) {
         if (LOG.isLoggable(Level.FINE)) {
           LOG.fine(
               "Object with key "
                   + element.getObjectKey()
                   + " is not Serializable and is not being overflowed to disk.");
         }
       } else {
         spoolToDisk(element);
         // Don't notify listeners. They are not being removed from the cache, only a store
         remove(keys[i]);
       }
     }
   }
 }
  /**
   * {@inheritDoc}
   *
   * <p>This implementation queues the put notification for in-order replication to peers.
   *
   * @param cache the cache emitting the notification
   * @param element the element which was just put into the cache.
   */
  public final void notifyElementPut(final Ehcache cache, final Element element)
      throws CacheException {
    if (notAlive()) {
      return;
    }

    if (!replicatePuts) {
      return;
    }

    if (!element.isSerializable()) {
      if (LOG.isWarnEnabled()) {
        LOG.warn(
            "Object with key "
                + element.getObjectKey()
                + " is not Serializable and cannot be replicated");
      }
      return;
    }
    addToReplicationQueue(new CacheEventMessage(EventMessage.PUT, cache, element, null));
  }
예제 #23
0
  /**
   * Evict the <code>Element</code>.
   *
   * <p>Evict means that the <code>Element</code> is:
   *
   * <ul>
   *   <li>if, the store is diskPersistent, the <code>Element</code> is spooled to the DiskStore
   *   <li>if not, the <code>Element</code> is removed.
   * </ul>
   *
   * @param element the <code>Element</code> to be evicted.
   */
  protected final void evict(Element element) throws CacheException {
    boolean spooled = false;
    if (cache.getCacheConfiguration().isOverflowToDisk()) {
      if (!element.isSerializable()) {
        if (LOG.isLoggable(Level.FINE)) {
          LOG.log(
              Level.FINE,
              new StringBuffer("Object with key ")
                  .append(element.getObjectKey())
                  .append(" is not Serializable and cannot be overflowed to disk")
                  .toString());
        }
      } else {
        spoolToDisk(element);
        spooled = true;
      }
    }

    if (!spooled) {
      cache.getCacheEventNotificationService().notifyElementEvicted(element, false);
    }
  }
예제 #24
0
 /** {@inheritDoc} */
 public Object getKey() {
   return element.getObjectKey();
 }
예제 #25
0
 private void possiblyTriggerRefresh(Element elem, long timeToRefreshMillis) {
   if (checkForRefresh(elem, System.currentTimeMillis(), timeToRefreshMillis)) {
     // now add the key to the queue. smallest overhead we could get.
     refreshWorkQueue.offer(elem.getObjectKey());
   }
 }
예제 #26
0
 /**
  * Puts the element in the DiskStore. Should only be called if overflowToDisk is true
  *
  * <p>Relies on being called from a synchronized method
  *
  * @param element The Element
  */
 protected void spoolToDisk(Element element) {
   diskStore.put(element);
   if (LOG.isLoggable(Level.FINE)) {
     LOG.fine(cache.getName() + "Cache: spool to disk done for: " + element.getObjectKey());
   }
 }
  @Test
  public void testTimeToLive() {
    Assert.assertEquals(_VALUE_1, _ehcachePortalCache.get(_KEY_1));
    Assert.assertNull(_ehcachePortalCache.get(_KEY_2));

    // Put

    int timeToLive = 600;

    _ehcachePortalCache.put(_KEY_2, _VALUE_2, timeToLive);

    Ehcache ehcache = _ehcachePortalCache.ehcache;

    Element element = ehcache.get(_KEY_2);

    Assert.assertEquals(_KEY_2, element.getObjectKey());
    Assert.assertEquals(_VALUE_2, element.getObjectValue());
    Assert.assertEquals(timeToLive, element.getTimeToLive());

    _defaultCacheListener.assertPut(_KEY_2, _VALUE_2, timeToLive);

    _defaultCacheListener.reset();

    // Put if absent

    ehcache.removeElement(element);

    _ehcachePortalCache.putIfAbsent(_KEY_2, _VALUE_2, timeToLive);

    element = ehcache.get(_KEY_2);

    Assert.assertEquals(_KEY_2, element.getObjectKey());
    Assert.assertEquals(_VALUE_2, element.getObjectValue());
    Assert.assertEquals(timeToLive, element.getTimeToLive());

    _defaultCacheListener.assertPut(_KEY_2, _VALUE_2, timeToLive);

    _defaultCacheListener.reset();

    // Replace 1

    ehcache.removeElement(element);

    _ehcachePortalCache.replace(_KEY_1, _VALUE_2, timeToLive);

    element = ehcache.get(_KEY_1);

    Assert.assertEquals(_KEY_1, element.getObjectKey());
    Assert.assertEquals(_VALUE_2, element.getObjectValue());
    Assert.assertEquals(timeToLive, element.getTimeToLive());

    _defaultCacheListener.assertUpdated(_KEY_1, _VALUE_2, timeToLive);

    _defaultCacheListener.reset();

    // Replace 2

    ehcache.removeElement(element);

    _ehcachePortalCache.put(_KEY_1, _VALUE_1);

    Assert.assertEquals(_VALUE_1, _ehcachePortalCache.get(_KEY_1));
    Assert.assertNull(_ehcachePortalCache.get(_KEY_2));

    _ehcachePortalCache.replace(_KEY_1, _VALUE_1, _VALUE_2, timeToLive);

    element = ehcache.get(_KEY_1);

    Assert.assertEquals(_KEY_1, element.getObjectKey());
    Assert.assertEquals(_VALUE_2, element.getObjectValue());
    Assert.assertEquals(timeToLive, element.getTimeToLive());

    _defaultCacheListener.assertPut(_KEY_1, _VALUE_1);
    _defaultCacheListener.assertUpdated(_KEY_1, _VALUE_2, timeToLive);

    _defaultCacheListener.reset();
  }
예제 #28
0
 /**
  * Puts an item in the cache. Note that this automatically results in an eviction if the store is
  * full.
  *
  * @param element the element to add
  */
 public final void put(Element element) throws CacheException {
   if (element != null) {
     map.put(element.getObjectKey(), element);
     doPut(element);
   }
 }