/**
   * 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()));
    }
  }
  public void testSimultaneousPutRemove() throws InterruptedException {
    cacheName = SAMPLE_CACHE2; // Synced one
    Ehcache cache1 = manager1.getEhcache(cacheName);
    Ehcache cache2 = manager2.getEhcache(cacheName);

    Serializable key = "1";
    Serializable value = new Date();
    Element element = new Element(key, value);

    // Put
    cache1.put(element);
    Thread.currentThread().sleep(1000);
    cache2.remove(element.getKey());
    Thread.currentThread().sleep(1000);

    assertNull(cache1.get(element.getKey()));
    manager1.clearAll();
    Thread.currentThread().sleep(1000);

    cache2.put(element);
    cache2.remove(element.getKey());
    Thread.currentThread().sleep(1000);
    cache1.put(element);
    Thread.currentThread().sleep(1000);
    assertNotNull(cache2.get(element.getKey()));

    manager1.clearAll();
    Thread.currentThread().sleep(1000);
  }
Example #3
0
 private boolean evict() {
   Element remove = null;
   writeLock().lock();
   try {
     Element evict = nextExpiredOrToEvict(null);
     if (evict != null) {
       remove = remove(evict.getKey(), hash(evict.getKey().hashCode()), null);
     }
   } finally {
     writeLock().unlock();
   }
   notifyEvictionOrExpiry(remove);
   return remove != null;
 }
Example #4
0
 private boolean internalPut(final StorePutCommand putCommand) {
   final Element element = putCommand.getElement();
   boolean isNull;
   if (element == null) {
     return true;
   }
   XATransactionContext context = getOrCreateTransactionContext();
   // In case this key is currently being updated...
   isNull = underlyingStore.get(element.getKey()) == null;
   if (isNull) {
     isNull = context.get(element.getKey()) == null;
   }
   context.addCommand(putCommand, element);
   return isNull;
 }
  @Test
  public void testRefreshElement() throws Exception {
    final IncrementingCacheEntryFactory factory = new IncrementingCacheEntryFactory();
    selfPopulatingCache = new SelfPopulatingCache(cache, factory);

    Element e1 = selfPopulatingCache.get("key1");
    Element e2 = selfPopulatingCache.get("key2");
    assertEquals(2, selfPopulatingCache.getSize());
    assertEquals(2, factory.getCount());
    assertEquals(Integer.valueOf(1), e1.getValue());
    assertEquals(Integer.valueOf(2), e2.getValue());

    // full refresh
    selfPopulatingCache.refresh();
    e1 = selfPopulatingCache.get("key1");
    e2 = selfPopulatingCache.get("key2");
    assertEquals(2, selfPopulatingCache.getSize());
    assertEquals(4, factory.getCount());
    // we cannot be sure which order key1 or key2 gets refreshed,
    // as the implementation makes no guarantee over the sequence
    // of the refresh; all we can be sure of is that between
    // them key1&2 must have the values 3 & 4
    int e1i = ((Integer) e1.getValue()).intValue();
    int e2i = ((Integer) e2.getValue()).intValue();
    assertTrue(((e1i == 3) && (e2i == 4)) || ((e1i == 4) && (e2i == 3)));

    // single element refresh
    selfPopulatingCache.get("key2");
    Element e2r = selfPopulatingCache.refresh("key2");
    assertEquals(2, selfPopulatingCache.getSize());
    assertEquals(5, factory.getCount());
    assertNotNull(e2r);
    assertEquals("key2", e2r.getKey());
    assertEquals(Integer.valueOf(5), e2r.getValue());

    // additional element
    Element e3 = selfPopulatingCache.get("key3");
    assertEquals(3, selfPopulatingCache.getSize());
    assertEquals(6, factory.getCount());
    assertNotNull(e3);
    assertEquals("key3", e3.getKey());
    assertEquals(Integer.valueOf(6), e3.getValue());

    // full refresh
    selfPopulatingCache.refresh();
    assertEquals(3, selfPopulatingCache.getSize());
    assertEquals(9, factory.getCount());
  }
  @Test
  public void testSimultaneousPutRemove() throws InterruptedException {
    LOG.info("START TEST");

    // Synced one
    cacheName = SAMPLE_CACHE2;
    Ehcache cache1 = manager1.getEhcache(cacheName);
    Ehcache cache2 = manager2.getEhcache(cacheName);

    Serializable key = "1";
    Serializable value = new Date();
    Element element = new Element(key, value);

    // Put
    cache1.put(element);

    // Wait up to 2 seconds for the caches to become coherent
    CacheTestUtilities.waitForReplication(1, MAX_WAIT_TIME, cache2);

    cache2.remove(element.getKey());

    // Wait up to 2 seconds for the caches to become coherent
    CacheTestUtilities.waitForReplication(0, MAX_WAIT_TIME, cache1);

    assertNull(cache1.get(element.getKey()));
    manager1.clearAll();

    // Wait up to 2 seconds for the caches to become coherent
    CacheTestUtilities.waitForReplication(0, MAX_WAIT_TIME, cache2);

    cache2.put(element);
    cache2.remove(element.getKey());

    // Wait up to 2 seconds for the caches to become coherent
    CacheTestUtilities.waitForReplication(0, MAX_WAIT_TIME, cache2);

    cache1.put(element);

    // Wait up to 2 seconds for the caches to become coherent
    CacheTestUtilities.waitForReplication(1, MAX_WAIT_TIME, cache2);

    assertNotNull(cache2.get(element.getKey()));

    manager1.clearAll();

    LOG.info("END TEST");
  }
Example #7
0
  /** {@inheritDoc} */
  public Element replace(Element element) throws NullPointerException {
    LOG.debug("cache {} replace1 {}", cache.getName(), element);
    XATransactionContext context = getOrCreateTransactionContext();
    Element previous = getCurrentElement(element.getKey(), context);

    if (previous != null) {
      Element oldElement = getQuietFromUnderlyingStore(element.getObjectKey());
      Element elementForWrite = copyElementForWrite(element);
      context.addCommand(new StorePutCommand(oldElement, elementForWrite), elementForWrite);
    }
    return copyElementForRead(previous);
  }
Example #8
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;
  }
 private void flushDependenciesOfPath(
     Cache depCache, Set<String> flushed, String path, boolean propageToOtherClusterNodes) {
   if (logger.isDebugEnabled()) {
     logger.debug("Flushing dependencies for path : " + path);
   }
   Element element = !flushed.contains(path) ? depCache.get(path) : null;
   if (element != null) {
     flushed.add(path);
     if (logger.isDebugEnabled()) {
       logger.debug("Flushing path : " + path);
     }
     cacheProvider.invalidate(path, propageToOtherClusterNodes);
     depCache.remove(element.getKey());
   }
 }
Example #10
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;
  }
  /**
   * 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()));
  }
Example #12
0
  /**
   * Constructor which takes an Ehcache core Element.
   *
   * <p>The {@link #mimeType} and {@link #value} are stored in the core Ehcache <code>value</code>
   * field using {@link net.sf.ehcache.MimeTypeByteArray}
   *
   * <p>If the MIME Type is not set, an attempt is made to set a sensible value. The rules for
   * setting the Mime Type are:
   *
   * <ol>
   *   <li>If the value in element is null, the <code>mimeType</code> is set to null.
   *   <li>If we stored the mimeType in ehcache, then <code>mimeType</code> is set with it.
   *   <li>If no mimeType was set and the value is a <code>byte[]</code> the <code>mimeType</code>
   *       is set to "application/octet-stream".
   *   <li>If no mimeType was set and the value is a <code>String</code> the <code>mimeType</code>
   *       is set to "text/plain".
   * </ol>
   *
   * @param element the ehcache core Element
   * @throws CacheException if an Exception occurred in the underlying cache.
   */
  public Element(net.sf.ehcache.Element element) throws CacheException {

    key = element.getKey();
    expirationDate = element.getExpirationTime();

    Object ehcacheValue = element.getObjectValue();

    if (ehcacheValue == null) {
      this.value = null;
      this.mimeType = null;
    }
    if (ehcacheValue instanceof MimeTypeByteArray) {
      // we have Mime Type data to extract
      mimeType = ((MimeTypeByteArray) ehcacheValue).getMimeType();
      if (mimeType == null) {
        // Jersey is returning */* for these which I think is wrong. Reported to Paul Sandoz.
        mimeType = "application/octet-stream";
      }
      this.value = ((MimeTypeByteArray) ehcacheValue).getValue();

    } else if (ehcacheValue instanceof byte[]) {
      // already a byte[]
      this.value = (byte[]) ehcacheValue;
      mimeType = "application/octet-stream";
    } else if (ehcacheValue instanceof String) {
      // a String such as XML
      this.value = ((String) ehcacheValue).getBytes();
      this.mimeType = "text/plain";
    } else {
      // A type we do not handle therefore serialize using Java Serialization
      MemoryEfficientByteArrayOutputStream stream = null;
      try {
        stream = MemoryEfficientByteArrayOutputStream.serialize(element.getValue());
      } catch (IOException e) {
        throw new CacheException(e);
      }
      this.value = stream.getBytes();
      this.mimeType = "application/x-java-serialized-object";
    }
  }
  @Test
  public void testRefreshAbsentElement() throws Exception {
    final IncrementingCacheEntryFactory factory = new IncrementingCacheEntryFactory();
    selfPopulatingCache = new SelfPopulatingCache(cache, factory);

    selfPopulatingCache.get("key1");
    selfPopulatingCache.get("key2");
    assertEquals(2, selfPopulatingCache.getSize());
    assertEquals(2, factory.getCount());

    // full refresh
    selfPopulatingCache.refresh();
    assertEquals(2, selfPopulatingCache.getSize());
    assertEquals(4, factory.getCount());

    // single element refresh which is not in the cache
    Element e3 = selfPopulatingCache.refresh("key3");
    assertEquals(3, selfPopulatingCache.getSize());
    assertEquals(5, factory.getCount());
    assertNotNull(e3);
    assertEquals("key3", e3.getKey());
    assertEquals(Integer.valueOf(5), e3.getValue());
  }
Example #14
0
 public void put(CasAuthentication token) {
   Element element = new Element(token.getCredentials().toString(), token);
   logger.debug("Cache put: {}", element.getKey());
   cache.put(element);
 }
Example #15
0
    protected Element put(
        Object key,
        int hash,
        Element value,
        long sizeOf,
        boolean onlyIfAbsent,
        boolean pinned,
        boolean fire) {
      Element[] evicted = new Element[MAX_EVICTION];
      writeLock().lock();
      try {
        int c = count;
        if (c++ > threshold) // ensure capacity
        rehash();
        HashEntry[] tab = table;
        int index = hash & (tab.length - 1);
        HashEntry first = tab[index];
        HashEntry e = first;
        while (e != null && (e.hash != hash || !key.equals(e.key))) e = e.next;

        Element oldValue;
        if (e != null) {
          oldValue = e.value;
          if (e.value == DUMMY_PINNED_ELEMENT || !onlyIfAbsent) {
            poolAccessor.delete(e.sizeOf);
            e.value = value;
            e.sizeOf = sizeOf;
            if (oldValue == DUMMY_PINNED_ELEMENT && value != DUMMY_PINNED_ELEMENT) {
              --numDummyPinnedKeys;
              oldValue = null;
            }
            if (fire) {
              postInstall(key, value, e.pinned);
            }
          }
        } else {
          oldValue = null;
          ++modCount;
          tab[index] = createHashEntry(key, hash, first, value, sizeOf, pinned);
          count = c; // write-volatile
          if (fire) {
            postInstall(key, value, pinned);
          }
        }

        if (!pinned && (onlyIfAbsent && oldValue != null || !onlyIfAbsent)) {
          if (!isPinned(key, hash)) {
            this.fullyPinned = false;
          }
          if (!fullyPinned && SelectableConcurrentHashMap.this.maxSize > 0) {
            int runs =
                Math.min(
                    MAX_EVICTION,
                    SelectableConcurrentHashMap.this.quickSize()
                        - (int) SelectableConcurrentHashMap.this.maxSize);
            while (runs-- > 0) {
              Element evict = nextExpiredOrToEvict(value);
              if (evict != null) {
                Element removed;
                while ((removed = remove(evict.getKey(), hash(evict.getKey().hashCode()), null))
                    == null) {
                  evict = nextExpiredOrToEvict(value);
                  if (evict == null) {
                    break;
                  }
                }
                evicted[runs] = removed;
              }
            }
          }
        }
        return oldValue;
      } finally {
        writeLock().unlock();
        for (Element element : evicted) {
          notifyEvictionOrExpiry(element);
        }
      }
    }
 /**
  * Called immediately after an element is <i>found</i> to be expired. The {@link
  * net.sf.ehcache.Cache#remove(Object)} method will block until this method returns.
  *
  * <p>As the {@link net.sf.ehcache.Element} has been expired, only what was the key of the
  * element is known.
  *
  * <p>Elements are checked for expiry in ehcache at the following times:
  *
  * <ul>
  *   <li>When a get request is made
  *   <li>When an element is spooled to the diskStore in accordance with a MemoryStore eviction
  *       policy
  *   <li>In the DiskStore when the expiry thread runs, which by default is {@link
  *       net.sf.ehcache.Cache#DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS}
  * </ul>
  *
  * If an element is found to be expired, it is deleted and this method is notified.
  *
  * @param cache the cache emitting the notification
  * @param element the element that has just expired
  *     <p>Deadlock Warning: expiry will often come from the <code>DiskStore</code> expiry
  *     thread. It holds a lock to the DiskStorea the time the notification is sent. If the
  *     implementation of this method calls into a synchronized <code>Cache</code> method and
  *     that subsequently calls into DiskStore a deadlock will result. Accordingly implementers
  *     of this method should not call back into Cache.
  */
 public void notifyElementExpired(final Ehcache cache, final Element element) {
   LOG.info("Element expired : " + element);
   cache.put(new Element(element.getKey(), "set on notify", Boolean.TRUE, 0, 0));
   counter.incrementAndGet();
 }
Example #17
0
  /**
   * Check we get reasonable results for 2000 entries where entry 0 is accessed once increasing to
   * entry 1999 accessed 2000 times.
   *
   * <p>1 to 5000 population, with hit counts ranging from 1 to 500, not selecting lowest half. 5000
   * tests
   *
   * <p>Samples Cost No 7 38 99.24% confidence 8 27 99.46% confidence 9 10 10 11300 4 99.92%
   * confidence 12 2 20 11428 0 99.99% confidence
   *
   * <p>1 to 5000 population, with hit counts ranging from 1 to 500, not selecting lowest quarter.
   * 5000 tests S No 10 291 94.18% confidence 20 15 30 11536 1 99.99% confidence
   *
   * <p>For those with a statistical background the branch of stats which deals with this is
   * hypothesis testing and the Student's T distribution. The higher your sample the greater
   * confidence you can have in a hypothesis, in this case whether or not the "lowest" value lies in
   * the bottom half or quarter of the distribution. Adding samples rapidly increases confidence but
   * the return from extra sampling rapidly diminishes.
   *
   * <p>Cost is not affected much by sample size. Profiling shows that it is the iteration that is
   * causing most of the time. If we had access to the array backing Map, all would work very fast.
   * Still, it is fast enough.
   *
   * <p>A 99.99% confidence interval can be achieved that the "lowest" element is actually in the
   * bottom quarter of the hit count distribution.
   *
   * @throws java.io.IOException Performance: With a sample size of 10: 523ms for 5000 runs = 104 ?s
   *     per run With a sample size of 30: 628ms for 5000 runs = 125 ?s per run
   */
  @Test
  public void testLowest() throws IOException {
    createMemoryOnlyStore(MemoryStoreEvictionPolicy.LFU, 5000);
    // fully populate the otherwise we just find nulls
    for (int i = 0; i < 5000; i++) {
      Element newElement = new Element("" + i, new Date());
      store.put(newElement);
    }

    Element element = null;

    Element newElement = null;
    for (int i = 0; i < 10; i++) {
      newElement = new Element("" + i, new Date());
      store.put(newElement);
      int j;
      for (j = 0; j <= i; j++) {
        store.get("" + i);
      }
      if (i > 0) {
        try {
          element =
              (Element)
                  GET_EVICTION_TARGET.invoke(
                      PRIMARY_FACTORY.get(store), new Object(), Integer.MAX_VALUE);
        } catch (Exception e) {
          throw new RuntimeException(e);
        }
        assertTrue(!element.equals(newElement));
        assertTrue(element.getHitCount() < 2);
      }
    }

    int lowestQuarterNotIdentified = 0;

    long findTime = 0;
    StopWatch stopWatch = new StopWatch();
    for (int i = 10; i < 5000; i++) {
      store.put(new Element("" + i, new Date()));
      int j;
      int maximumHitCount = 0;
      for (j = 0; j <= i; j += 10) {
        store.get("" + i);
        maximumHitCount++;
      }

      stopWatch.getElapsedTime();
      try {
        element =
            (Element)
                GET_EVICTION_TARGET.invoke(
                    PRIMARY_FACTORY.get(store), new Object(), Integer.MAX_VALUE);
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
      findTime += stopWatch.getElapsedTime();
      long lowest = element.getHitCount();
      long bottomQuarter = (Math.round(maximumHitCount / 4.0) + 1);
      assertTrue(!element.equals(newElement));
      if (lowest > bottomQuarter) {
        LOG.info(
            ""
                + element.getKey()
                + " hit count: "
                + element.getHitCount()
                + " bottomQuarter: "
                + bottomQuarter);
        lowestQuarterNotIdentified++;
      }
    }
    LOG.info("Find time: " + findTime);
    assertTrue(findTime < 200);
    LOG.info("Selections not in lowest quartile: " + lowestQuarterNotIdentified);
    assertTrue(lowestQuarterNotIdentified <= 10);
  }