All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jsr107.ri.event.RICacheEventDispatcher Maven / Gradle / Ivy

There is a newer version: 62
Show newest version
/**
 *  Copyright 2011-2013 Terracotta, Inc.
 *  Copyright 2011-2013 Oracle America Incorporated
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.jsr107.ri.event;

import javax.cache.configuration.CacheEntryListenerConfiguration;
import javax.cache.event.CacheEntryCreatedListener;
import javax.cache.event.CacheEntryEvent;
import javax.cache.event.CacheEntryEventFilter;
import javax.cache.event.CacheEntryExpiredListener;
import javax.cache.event.CacheEntryListener;
import javax.cache.event.CacheEntryListenerException;
import javax.cache.event.CacheEntryRemovedListener;
import javax.cache.event.CacheEntryUpdatedListener;
import javax.cache.event.EventType;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Collects and appropriately dispatches {@link CacheEntryEvent}s to
 * {@link CacheEntryListener}s.
 *
 * @param  the type of keys
 * @param  the type of values
 * @author Brian Oliver
 */
public class RICacheEventDispatcher {

  /**
   * The map of {@link CacheEntryEvent}s to deliver, keyed by the class of
   * {@link CacheEntryListener} to which they should be dispatched.
   */
  private ConcurrentHashMap,
      ArrayList>> eventMap;

  /**
   * Constructs an {@link RICacheEventDispatcher}.
   */
  public RICacheEventDispatcher() {
    this.eventMap = new ConcurrentHashMap,
        ArrayList>>();
  }

  /**
   * Requests that the specified event be prepared for dispatching to the
   * specified type of listeners.
   *
   * @param listenerClass the class of {@link CacheEntryListener} that should
   *                      receive the event
   * @param event         the event to be dispatched
   */
  public void addEvent(Class listenerClass,
                       CacheEntryEvent event) {
    if (listenerClass == null) {
      throw new NullPointerException("listenerClass can't be null");
    }

    if (event == null) {
      throw new NullPointerException("event can't be null");
    }

    if (!listenerClass.isInterface() || !CacheEntryListener.class.isAssignableFrom(listenerClass)) {
      throw new IllegalArgumentException("listenerClass must be an CacheEntryListener interface");
    }

    //for safety
    ArrayList> eventList;
    synchronized (this) {
      eventList = eventMap.get(listenerClass);
      if (eventList == null) {
        eventList = new ArrayList>();
        eventMap.put(listenerClass, eventList);
      }
    }

    eventList.add(event);
  }

  /**
   * Dispatches the added events to the listeners defined by the specified
   * {@link CacheEntryListenerConfiguration}s.
   *
   * @param registrations the {@link RICacheEntryListenerRegistration}s defining
   *                      {@link CacheEntryListener}s to which to dispatch events
   * @see #addEvent(Class, CacheEntryEvent)
   */
  public void dispatch(Iterable> registrations) {

    //TODO: we could really optimize this implementation

    //TODO: we need to handle exceptions here

    //TODO: we need to work out which events should be raised synchronously or asynchronously

    //TODO: we need to remove/hide old values appropriately

    Iterable> events;

    try {

      //notify expiry listeners
      events = eventMap.get(CacheEntryExpiredListener.class);
      if (events != null) {
        for (RICacheEntryListenerRegistration registration : registrations) {
          CacheEntryEventFilter filter = registration.getCacheEntryFilter();
          Iterable> iterable =
              filter == null ? events : new RICacheEntryEventFilteringIterable(events, filter);

          CacheEntryListener listener = registration.getCacheEntryListener();
          if (listener instanceof CacheEntryExpiredListener) {
            ((CacheEntryExpiredListener) listener).onExpired(cloneEvents(registration, iterable));
          }
        }
      }

      //notify create listeners
      events = eventMap.get(CacheEntryCreatedListener.class);
      if (events != null) {
        for (RICacheEntryListenerRegistration registration : registrations) {
          CacheEntryEventFilter filter = registration.getCacheEntryFilter();
          Iterable> iterable =
              filter == null ? events : new RICacheEntryEventFilteringIterable(events, filter);

          CacheEntryListener listener = registration.getCacheEntryListener();
          if (listener instanceof CacheEntryCreatedListener) {
            ((CacheEntryCreatedListener) listener).onCreated(iterable);
          }
        }
      }

      //notify update listeners
      events = eventMap.get(CacheEntryUpdatedListener.class);
      if (events != null) {
        for (RICacheEntryListenerRegistration registration : registrations) {
          CacheEntryEventFilter filter = registration.getCacheEntryFilter();
          Iterable> iterable =
              filter == null ? events : new RICacheEntryEventFilteringIterable(events, filter);

          CacheEntryListener listener = registration.getCacheEntryListener();
          if (listener instanceof CacheEntryUpdatedListener) {
            ((CacheEntryUpdatedListener) listener).onUpdated(cloneEvents(registration, iterable));
          }
        }
      }

      //notify remove listeners
      events = eventMap.get(CacheEntryRemovedListener.class);
      if (events != null) {
        for (RICacheEntryListenerRegistration registration : registrations) {
          CacheEntryEventFilter filter = registration.getCacheEntryFilter();
          Iterable> iterable =
              filter == null ? events : new RICacheEntryEventFilteringIterable(events, filter);

          CacheEntryListener listener = registration.getCacheEntryListener();
          if (listener instanceof CacheEntryRemovedListener) {
            ((CacheEntryRemovedListener) listener).onRemoved(cloneEvents(registration, iterable));
          }
        }
      }
    } catch (Exception e) {
      if (!(e instanceof CacheEntryListenerException)) {
        throw new CacheEntryListenerException("Exception on listener execution", e);
      } else {
        throw e;
      }
    }
  }

  private List> cloneEvents(RICacheEntryListenerRegistration registration,
                                                  Iterable> events) {
    List> dispatchedEvents = new ArrayList>();
    // clone events, setting or not the old value depending on registration properties
    for (CacheEntryEvent event : events) {
      RICacheEntryEvent dispatchedEvent;
      if (registration.isOldValueRequired()) {
        dispatchedEvent = new RICacheEntryEvent(event.getSource(), event.getKey(), event.getValue(), event.getOldValue(), event.getEventType());
      } else {
        if (event.getEventType() == EventType.REMOVED || event.getEventType() == EventType.EXPIRED) {
          // Since JCache 1.1, removed & expired events have to return oldValue or null when oldValueRequired == false
          // RI chooses to return null as old value and oldValueAvailable is false in this case
          dispatchedEvent = new RICacheEntryEvent(event.getSource(), event.getKey(), null, null,
                  event.getEventType(), false);
        } else {
          dispatchedEvent = new RICacheEntryEvent(event.getSource(), event.getKey(), event.getValue(), null,
                  event.getEventType(), false);
        }
      }
      dispatchedEvents.add(dispatchedEvent);
    }
    return dispatchedEvents;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy