@Test
 public void testGetEvents() {
   Set<Class<? extends Event>> eventClasses = new HashSet<Class<? extends Event>>();
   for (Event theEvent : listener.getEvents()) {
     eventClasses.add(theEvent.getClass());
   }
   assertEquals(2, eventClasses.size());
   assertTrue(
       "Expecting registration for DocumentUpdatingEvent",
       eventClasses.contains(DocumentUpdatingEvent.class));
   assertTrue(
       "Expecting registration for DocumentUpdatedEvent events",
       eventClasses.contains(DocumentUpdatedEvent.class));
 }
 @Override
 public void addEvent(String listenerName, Event event) {
   Map<String, RegisteredListener> listeners = getListenersByEvent().get(event.getClass());
   if (listeners == null) {
     listeners = new ConcurrentHashMap<String, RegisteredListener>();
     this.listenersByEvent.put(event.getClass(), listeners);
   }
   RegisteredListener listener = listeners.get(listenerName);
   if (listener != null) {
     listener.addEvent(event);
   } else {
     listeners.put(listenerName, new RegisteredListener(this.getListener(listenerName), event));
   }
 }
  @Override
  public void notify(Event event, Object source, Object data) {
    // Find all listeners for this event
    Map<String, RegisteredListener> regListeners = getListenersByEvent().get(event.getClass());
    if (regListeners != null) {
      notify(regListeners.values(), event, source, data);
    }

    // Find listener listening all events
    Map<String, RegisteredListener> allEventRegListeners =
        this.listenersByEvent.get(AllEvent.class);
    if (allEventRegListeners != null) {
      notify(allEventRegListeners.values(), event, source, data);
    }

    // We want this Observation Manager to be able to handle new Event Listener components being
    // added or removed
    // at runtime. Thus ideally we should make this Manager an Event Listener itself. However in
    // order to avoid
    // circular dependencies issues and in order to be more performant we simply handle
    // ComponentDescriptorEvents
    // here to add/remove Event Listeners.
    if (event instanceof ComponentDescriptorEvent) {
      onComponentEvent(
          (ComponentDescriptorEvent) event,
          (ComponentManager) source,
          (ComponentDescriptor<EventListener>) data);
    }
  }
 @Override
 public void removeEvent(String listenerName, Event event) {
   Map<String, RegisteredListener> listeners = getListenersByEvent().get(event.getClass());
   RegisteredListener listener = listeners.get(listenerName);
   if (listener != null) {
     listener.removeEvent(event);
   }
 }
  @Override
  public void addListener(EventListener eventListener) {
    // Register the listener by name. If already registered, override it.
    EventListener previousListener =
        getListenersByName().put(eventListener.getName(), eventListener);

    // If the passed event listener name is already registered, log a warning
    if (previousListener != null) {
      this.logger.warn(
          "The [{}] listener has overwritten a previously "
              + "registered listener [{}] since they both are registered under the same id [{}]. "
              + "In the future consider removing a Listener first if you really want to register it again.",
          new Object[] {
            eventListener.getClass().getName(),
            previousListener.getClass().getName(),
            eventListener.getName()
          });
    }

    // For each event defined for this listener, add it to the Event Map.
    for (Event event : eventListener.getEvents()) {
      // Check if this is a new Event type not already registered
      Map<String, RegisteredListener> eventListeners = this.listenersByEvent.get(event.getClass());
      if (eventListeners == null) {
        // No listener registered for this event yet. Create a map to store listeners for this
        // event.
        eventListeners = new ConcurrentHashMap<String, RegisteredListener>();
        this.listenersByEvent.put(event.getClass(), eventListeners);
        // There is no RegisteredListener yet, create one
        eventListeners.put(eventListener.getName(), new RegisteredListener(eventListener, event));
      } else {
        // Add an event to existing RegisteredListener object
        RegisteredListener registeredListener = eventListeners.get(eventListener.getName());
        if (registeredListener == null) {
          eventListeners.put(eventListener.getName(), new RegisteredListener(eventListener, event));
        } else {
          registeredListener.addEvent(event);
        }
      }
    }
  }
  /**
   * Call the provided listeners matching the passed Event. The definition of <em>source</em> and
   * <em>data</em> is purely up to the communicating classes.
   *
   * @param listeners the listeners to notify
   * @param event the event to pass to the registered listeners
   * @param source the source of the event (or <code>null</code>)
   * @param data the additional data related to the event (or <code>null</code>)
   */
  private void notify(
      Collection<RegisteredListener> listeners, Event event, Object source, Object data) {
    for (RegisteredListener listener : listeners) {
      // Verify that one of the events matches and send the first matching event
      for (Event listenerEvent : listener.events) {
        if (listenerEvent.matches(event)) {
          try {
            listener.listener.onEvent(event, source, data);
          } catch (Exception e) {
            // protect from bad listeners
            this.logger.error(
                "Failed to send event [{}] to listener [{}]",
                new Object[] {event, listener.listener, e});
          }

          // Only send the first matching event since the listener should only be called once per
          // event.
          break;
        }
      }
    }
  }