@Override
 public void onRemoval(RemovalNotification<Key, Value> notification) {
   if (notification.getKey() == null) {
     return;
   }
   notification.getKey().shard.requestCache().onRemoval(notification);
 }
Example #2
0
  @GwtIncompatible("removalListener")
  public void testRemovalNotification_clear() throws InterruptedException {
    // If a clear() happens while a computation is pending, we should not get a removal
    // notification.

    final AtomicBoolean shouldWait = new AtomicBoolean(false);
    final CountDownLatch computingLatch = new CountDownLatch(1);
    CacheLoader<String, String> computingFunction =
        new CacheLoader<String, String>() {
          @Override
          public String load(String key) throws InterruptedException {
            if (shouldWait.get()) {
              computingLatch.await();
            }
            return key;
          }
        };
    QueuingRemovalListener<String, String> listener = queuingRemovalListener();

    final LoadingCache<String, String> cache =
        CacheBuilder.newBuilder()
            .concurrencyLevel(1)
            .removalListener(listener)
            .build(computingFunction);

    // seed the map, so its segment's count > 0
    cache.getUnchecked("a");
    shouldWait.set(true);

    final CountDownLatch computationStarted = new CountDownLatch(1);
    final CountDownLatch computationComplete = new CountDownLatch(1);
    new Thread(
            new Runnable() {
              @Override
              public void run() {
                computationStarted.countDown();
                cache.getUnchecked("b");
                computationComplete.countDown();
              }
            })
        .start();

    // wait for the computingEntry to be created
    computationStarted.await();
    cache.invalidateAll();
    // let the computation proceed
    computingLatch.countDown();
    // don't check cache.size() until we know the get("b") call is complete
    computationComplete.await();

    // At this point, the listener should be holding the seed value (a -> a), and the map should
    // contain the computed value (b -> b), since the clear() happened before the computation
    // completed.
    assertEquals(1, listener.size());
    RemovalNotification<String, String> notification = listener.remove();
    assertEquals("a", notification.getKey());
    assertEquals("a", notification.getValue());
    assertEquals(1, cache.size());
    assertEquals("b", cache.getUnchecked("b"));
  }
Example #3
0
 @Override
 public void onRemoval(RemovalNotification<Serializable, Session> notification) {
   Serializable key = notification.getKey();
   Session session = notification.getValue();
   if (notification.getCause() == RemovalCause.EXPIRED) {
     // time out cause session expired.
     logger.info("session for {} expired.", session.getId());
   } else {
     // logout cause session be removed.
     logger.info("session for {} stoped.", session.getId());
   }
   Object attribute = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
   if (attribute instanceof PrincipalCollection) {
     PrincipalCollection collection = (PrincipalCollection) attribute;
     for (Object object : collection) {
       if (object instanceof ShiroPrincipal) {
         ShiroPrincipal shiroPrincipal = (ShiroPrincipal) object;
         UsrSession userSession = shiroPrincipal.getSession();
         userSession.setLastAccessTime(new Timestamp(session.getLastAccessTime().getTime()));
         userSession.setStopTime(new Timestamp(System.currentTimeMillis()));
         UsrSession merge = userSessionDao.update(userSession);
         shiroPrincipal.setSession(merge);
       }
     }
   }
 }
 @Override
 public void onRemoval(RemovalNotification<String, String> notif) {
   logger.info(
       "Removing attribute(notifyOnlyifStatechanges) for "
           + notif.getKey()
           + " as it's "
           + notif.getCause());
 }
Example #5
0
 @Override
 public void onRemoval(RemovalNotification<String, OutputStream> notification) {
   try {
     log.debug("Closing {}", notification.getKey());
     notification.getValue().close();
   } catch (Throwable throwable) {
     log.warn("Unable to close logfile for {}", notification.getKey(), throwable);
   }
 }
 public void testEquals() {
   new EqualsTester()
       .addEqualityGroup(
           RemovalNotification.create("one", 1, RemovalCause.EXPLICIT),
           RemovalNotification.create("one", 1, RemovalCause.REPLACED))
       .addEqualityGroup(RemovalNotification.create("1", 1, RemovalCause.EXPLICIT))
       .addEqualityGroup(RemovalNotification.create("one", 2, RemovalCause.EXPLICIT))
       .testEquals();
 }
 @Override
 public void onRemoval(RemovalNotification<TypedDirectionNamedPath, Notification> event) {
   switch (event.getCause()) {
     case SIZE:
       {
         LOG.warn(
             String.format(
                 "Evicted notification '%s' with %d events",
                 event.getKey(), event.getValue().getCount()));
         break;
       }
     default:
       {
         break;
       }
   }
 }
 @Override
 public void onRemoval(
     RemovalNotification<String, InstallFuture> removalNotification) {
   switch (removalNotification.getCause()) {
     case EXPLICIT:
       break;
     case REPLACED:
     case COLLECTED:
     case EXPIRED:
     case SIZE:
       SettableFuture<Boolean> value = removalNotification.getValue().future;
       if (value != null) { // May have been GCed
         value.set(false);
       }
       break;
   }
 }
 @Override
 public void onRemoval(RemovalNotification<Serializable, WebSession> notification) {
   Serializable key = notification.getKey();
   WebSession session = notification.getValue();
   if (notification.getCause() == RemovalCause.EXPIRED) {
     // time out cause session expired.
     logger.info("session for {} expired.", session.getId());
   } else {
     // logout cause session be removed.
     logger.info("session for {} stoped.", session.getId());
   }
   session.stop();
   UsrSession userSession = session.getValue();
   userSession.setLastAccessTime(new Timestamp(session.getLastAccessTime().getTime()));
   userSession.setStopTime(new Timestamp(session.getStopTimestamp().getTime()));
   userSessionDao.update(userSession);
 }
 @Override
 public synchronized void onRemoval(RemovalNotification<String, Session> removal) {
   removal.getValue().close();
   ;
   logger.debug("Closed connection to {}.", removal.getKey().toString());
 }
Example #11
0
  /**
   * Calls get() repeatedly from many different threads, and tests that all of the removed entries
   * (removed because of size limits or expiration) trigger appropriate removal notifications.
   */
  @GwtIncompatible("removalListener")
  public void testRemovalNotification_get_basher() throws InterruptedException {
    int nTasks = 3000;
    int nThreads = 100;
    final int getsPerTask = 1000;
    final int nUniqueKeys = 10000;
    final Random random = new Random(); // Randoms.insecureRandom();

    QueuingRemovalListener<String, String> removalListener = queuingRemovalListener();
    final AtomicInteger computeCount = new AtomicInteger();
    final AtomicInteger exceptionCount = new AtomicInteger();
    final AtomicInteger computeNullCount = new AtomicInteger();
    CacheLoader<String, String> countingIdentityLoader =
        new CacheLoader<String, String>() {
          @Override
          public String load(String key) throws InterruptedException {
            int behavior = random.nextInt(4);
            if (behavior == 0) { // throw an exception
              exceptionCount.incrementAndGet();
              throw new RuntimeException("fake exception for test");
            } else if (behavior == 1) { // return null
              computeNullCount.incrementAndGet();
              return null;
            } else if (behavior == 2) { // slight delay before returning
              Thread.sleep(5);
              computeCount.incrementAndGet();
              return key;
            } else {
              computeCount.incrementAndGet();
              return key;
            }
          }
        };
    final LoadingCache<String, String> cache =
        CacheBuilder.newBuilder()
            .concurrencyLevel(2)
            .expireAfterWrite(100, TimeUnit.MILLISECONDS)
            .removalListener(removalListener)
            .maximumSize(5000)
            .build(countingIdentityLoader);

    ExecutorService threadPool = Executors.newFixedThreadPool(nThreads);
    for (int i = 0; i < nTasks; i++) {
      threadPool.submit(
          new Runnable() {
            @Override
            public void run() {
              for (int j = 0; j < getsPerTask; j++) {
                try {
                  cache.getUnchecked("key" + random.nextInt(nUniqueKeys));
                } catch (RuntimeException e) {
                }
              }
            }
          });
    }

    threadPool.shutdown();
    threadPool.awaitTermination(300, TimeUnit.SECONDS);

    // Since we're not doing any more cache operations, and the cache only expires/evicts when doing
    // other operations, the cache and the removal queue won't change from this point on.

    // Verify that each received removal notification was valid
    for (RemovalNotification<String, String> notification : removalListener) {
      assertEquals("Invalid removal notification", notification.getKey(), notification.getValue());
    }

    CacheStats stats = cache.stats();
    assertEquals(removalListener.size(), stats.evictionCount());
    assertEquals(computeCount.get(), stats.loadSuccessCount());
    assertEquals(exceptionCount.get() + computeNullCount.get(), stats.loadExceptionCount());
    // each computed value is still in the cache, or was passed to the removal listener
    assertEquals(computeCount.get(), cache.size() + removalListener.size());
  }
Example #12
0
  /**
   * This is a less carefully-controlled version of {@link #testRemovalNotification_clear} - this is
   * a black-box test that tries to create lots of different thread-interleavings, and asserts that
   * each computation is affected by a call to {@code clear()} (and therefore gets passed to the
   * removal listener), or else is not affected by the {@code clear()} (and therefore exists in the
   * cache afterward).
   */
  @GwtIncompatible("removalListener")
  public void testRemovalNotification_clear_basher() throws InterruptedException {
    // If a clear() happens close to the end of computation, one of two things should happen:
    // - computation ends first: the removal listener is called, and the cache does not contain the
    //   key/value pair
    // - clear() happens first: the removal listener is not called, and the cache contains the pair
    AtomicBoolean computationShouldWait = new AtomicBoolean();
    CountDownLatch computationLatch = new CountDownLatch(1);
    QueuingRemovalListener<String, String> listener = queuingRemovalListener();
    final LoadingCache<String, String> cache =
        CacheBuilder.newBuilder()
            .removalListener(listener)
            .concurrencyLevel(20)
            .build(new DelayingIdentityLoader<String>(computationShouldWait, computationLatch));

    int nThreads = 100;
    int nTasks = 1000;
    int nSeededEntries = 100;
    Set<String> expectedKeys = Sets.newHashSetWithExpectedSize(nTasks + nSeededEntries);
    // seed the map, so its segments have a count>0; otherwise, clear() won't visit the in-progress
    // entries
    for (int i = 0; i < nSeededEntries; i++) {
      String s = "b" + i;
      cache.getUnchecked(s);
      expectedKeys.add(s);
    }
    computationShouldWait.set(true);

    final AtomicInteger computedCount = new AtomicInteger();
    ExecutorService threadPool = Executors.newFixedThreadPool(nThreads);
    final CountDownLatch tasksFinished = new CountDownLatch(nTasks);
    for (int i = 0; i < nTasks; i++) {
      final String s = "a" + i;
      threadPool.submit(
          new Runnable() {
            @Override
            public void run() {
              cache.getUnchecked(s);
              computedCount.incrementAndGet();
              tasksFinished.countDown();
            }
          });
      expectedKeys.add(s);
    }

    computationLatch.countDown();
    // let some computations complete
    while (computedCount.get() < nThreads) {
      Thread.yield();
    }
    cache.invalidateAll();
    tasksFinished.await();

    // Check all of the removal notifications we received: they should have had correctly-associated
    // keys and values. (An earlier bug saw removal notifications for in-progress computations,
    // which had real keys with null values.)
    Map<String, String> removalNotifications = Maps.newHashMap();
    for (RemovalNotification<String, String> notification : listener) {
      removalNotifications.put(notification.getKey(), notification.getValue());
      assertEquals(
          "Unexpected key/value pair passed to removalListener",
          notification.getKey(),
          notification.getValue());
    }

    // All of the seed values should have been visible, so we should have gotten removal
    // notifications for all of them.
    for (int i = 0; i < nSeededEntries; i++) {
      assertEquals("b" + i, removalNotifications.get("b" + i));
    }

    // Each of the values added to the map should either still be there, or have seen a removal
    // notification.
    assertEquals(expectedKeys, Sets.union(cache.asMap().keySet(), removalNotifications.keySet()));
    assertTrue(Sets.intersection(cache.asMap().keySet(), removalNotifications.keySet()).isEmpty());
  }