Package com.caucho.config.event

Source Code of com.caucho.config.event.EventManager$EventKey

/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT.  See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
*   Free Software Foundation, Inc.
*   59 Temple Place, Suite 330
*   Boston, MA 02111-1307  USA
*
* @author Scott Ferguson
*/

package com.caucho.config.event;

import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

import javax.enterprise.event.Observes;
import javax.enterprise.inject.Disposes;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedParameter;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.ObserverMethod;
import javax.inject.Inject;
import javax.inject.Qualifier;

import com.caucho.config.inject.InjectManager;
import com.caucho.config.reflect.BaseType;
import com.caucho.config.reflect.ParamType;
import com.caucho.inject.Module;
import com.caucho.util.L10N;

/**
* Internal implementation for a Bean
*/
@Module
public class EventManager
{
  private static final L10N L = new L10N(EventManager.class);
  private static final Logger log = Logger.getLogger(EventManager.class.getName());
 
  private InjectManager _cdiManager;

  private ConcurrentHashMap<Class<?>,ObserverMap> _extObserverMap
    = new ConcurrentHashMap<Class<?>,ObserverMap>();
 
  private ConcurrentHashMap<Class<?>,ObserverMap> _observerMap
    = new ConcurrentHashMap<Class<?>,ObserverMap>();

  private ConcurrentHashMap<EventKey,Set<ObserverMethod<?>>> _observerMethodCache
    = new ConcurrentHashMap<EventKey,Set<ObserverMethod<?>>>();
 
  public EventManager(InjectManager cdiManager)
  {
    _cdiManager = cdiManager;
  }

  public <X,Z> void addObserver(Bean<X> bean, AnnotatedMethod<Z> beanMethod)
  {
    int param = findObserverAnnotation(beanMethod);

    if (param < 0)
      return;

    Method method = beanMethod.getJavaMember();
    Type eventType = method.getGenericParameterTypes()[param];
   
    // ioc/0b22 vs ioc/0b0h
    /*
    if (! method.getDeclaringClass().equals(bean.getBeanClass())
        && ! bean.getBeanClass().isAnnotationPresent(Specializes.class)) {
      return;
    }
    */
   
    HashSet<Annotation> bindingSet = new HashSet<Annotation>();

    List<AnnotatedParameter<Z>> paramList = beanMethod.getParameters();
    for (Annotation ann : paramList.get(param).getAnnotations()) {
      if (ann.annotationType().isAnnotationPresent(Qualifier.class))
        bindingSet.add(ann);
    }
   
    Observes observes = paramList.get(param).getAnnotation(Observes.class);

    if (method.isAnnotationPresent(Inject.class)) {
      throw InjectManager.error(method, L.l("A method may not have both an @Observer and an @Inject annotation."));
    }

    if (method.isAnnotationPresent(Produces.class)) {
      throw InjectManager.error(method, L.l("A method may not have both an @Observer and a @Produces annotation."));
    }

    if (method.isAnnotationPresent(Disposes.class)) {
      throw InjectManager.error(method, L.l("A method may not have both an @Observer and a @Disposes annotation."));
    }

    ObserverMethodImpl<X,Z> observerMethod;
    switch(observes.during()) {
    case BEFORE_COMPLETION:
      observerMethod
        = new ObserverMethodBeforeCompletionImpl(_cdiManager,
                                                 bean,
                                                 beanMethod,
                                                 eventType,
                                                 bindingSet);
      break;
     
    case AFTER_COMPLETION:
      observerMethod
        = new ObserverMethodAfterCompletionImpl(_cdiManager,
                                                bean,
                                                beanMethod,
                                                eventType,
                                                bindingSet);
      break;
     
    case AFTER_SUCCESS:
      observerMethod
        = new ObserverMethodAfterSuccessImpl(_cdiManager,
                                             bean,
                                             beanMethod,
                                             eventType,
                                             bindingSet);
      break;
     
    case AFTER_FAILURE:
      observerMethod
        = new ObserverMethodAfterFailureImpl(_cdiManager,
                                             bean,
                                             beanMethod,
                                             eventType,
                                             bindingSet);
      break;
     
    default:
      observerMethod = new ObserverMethodImpl(_cdiManager, bean, beanMethod,
                                              eventType, bindingSet);
    }

    _cdiManager.addObserver(observerMethod, beanMethod);
  }

  public static <Z> int findObserverAnnotation(AnnotatedMethod<Z> method)
  {
    List<AnnotatedParameter<Z>> params = method.getParameters();
    int size = params.size();
    int observer = -1;

    for (int i = 0; i < size; i++) {
      AnnotatedParameter<?> param = params.get(i);

      if (param.isAnnotationPresent(Observes.class)) {
        if (observer >= 0 && observer != i)
          throw InjectManager.error(method.getJavaMember(), L.l("Only one param may have an @Observer"));

        observer = i;
      }
    }

    return observer;
  }
 
  public void fireEvent(Object event, Annotation... qualifiers)
  {
    for (ObserverMethod<?>  method : resolveObserverMethods(event, qualifiers)) {
      ((ObserverMethod) method).notify(event);
    }
  }
 
  public <T> Set<ObserverMethod<? super T>>
  resolveObserverMethods(T event, Annotation... qualifiers)
  {
    Class<?> eventClass = event.getClass();
   
    EventKey key = new EventKey(eventClass, qualifiers);
   
    Set<ObserverMethod<?>> observerList = _observerMethodCache.get(key);
   
    if (observerList == null) {
      BaseType eventType = _cdiManager.createSourceBaseType(event.getClass());

      // ioc/0b71
      if (eventType.isGeneric() || eventType instanceof ParamType)
        throw new IllegalArgumentException(L.l("'{0}' is an invalid event type because it's generic.",
                                               eventType));
     
      validateEventQualifiers(qualifiers);
           
      observerList = new LinkedHashSet<ObserverMethod<?>>();
   
      fillObserverMethodList(observerList, eventType, qualifiers);
   
      _observerMethodCache.put(key, observerList);
    }
   
    return (Set) observerList;
  }

  private void validateEventQualifiers(Annotation []qualifiers)
  {
    int length = qualifiers.length;
    for (int i = 0; i < length; i++) {
      Annotation qualifierA = qualifiers[i];

      Class<? extends Annotation> annType = qualifierA.annotationType();
   
      if (! _cdiManager.isQualifier(annType))
        throw new IllegalArgumentException(L.l("'{0}' is an invalid event annotation because it's not a @Qualifier.",
                                               qualifierA));
   
      Retention retention = annType.getAnnotation(Retention.class);
   
      if (retention == null || retention.value() != RetentionPolicy.RUNTIME) {
        throw new IllegalArgumentException(L.l("'{0}' is an invalid event qualifier because it doesn't have RUNTIME retention.",
                                               qualifierA));       
      }
   
      for (int j = i + 1; j < length; j++) {
        if (qualifierA.annotationType() == qualifiers[j].annotationType()) {
          throw new IllegalArgumentException(L.l("fireEvent is invalid because the bindings are duplicate types: {0} and {1}",
                                                 qualifiers[i], qualifiers[j]));
       
        }
      }
    }
  }

  public void
  fillObserverMethodList(Set<ObserverMethod<?>> list,
                         BaseType type, Annotation []qualifiers)
  {
    if (_cdiManager.getParent() != null) {
      EventManager parentManager = _cdiManager.getParent().getEventManager();
     
      parentManager.fillObserverMethodList(list, type, qualifiers);
    }
   
    fillLocalObserverList(_observerMap, list, type, qualifiers);
  }

  public void fireExtensionEvent(Object event,
                                 Annotation... qualifiers)
  {
    _cdiManager.getExtensionManager().updateExtensions();
    fireLocalEvent(_extObserverMap, event, qualifiers);
  }

  @Module
  public void fireExtensionEvent(Object event, BaseType eventType,
                                 Annotation... qualifiers)
  {
    _cdiManager.getExtensionManager().updateExtensions();
    fireLocalEvent(_extObserverMap, event, eventType, qualifiers);
  }

  private void fireLocalEvent(ConcurrentHashMap<Class<?>,ObserverMap> localMap,
                              Object event, Annotation... bindings)
  {
    // ioc/0062 - class with type-param handled specially
    BaseType eventType = _cdiManager.createTargetBaseType(event.getClass());
    fireLocalEvent(localMap, event, eventType, bindings);
  }
 
  private void fireLocalEvent(ConcurrentHashMap<Class<?>,ObserverMap> localMap,
                              Object event, BaseType eventType,
                              Annotation... qualifiers)
  {
    Set<ObserverMethod<?>> observerList = new LinkedHashSet<ObserverMethod<?>>();

    fillLocalObserverList(localMap, observerList, eventType, qualifiers);
    for (ObserverMethod<?> method : observerList) {
      ((ObserverMethod) method).notify(event);
    }
  }

  private void fillLocalObserverList(ConcurrentHashMap<Class<?>,ObserverMap> localMap,
                                     Set<ObserverMethod<?>> list,
                                     BaseType eventType,
                                     Annotation []qualifiers)
  {
    for (BaseType type : eventType.getBaseTypeClosure(_cdiManager)) {
      Class<?> rawClass = type.getRawClass();

      ObserverMap map = localMap.get(rawClass);

      if (map != null) {
        // ioc/0b5c, ioc/0b82
        map.resolveObservers((Set) list, eventType, qualifiers);
        map.resolveObservers((Set) list, type, qualifiers);
      }
    }
  }

  //
  // events
  //

  //
  // event management
  //

  /**
   * Registers an event observer
   *
   * @param observer the observer object
   * @param bindings the binding set for the event
   */
  public void addObserver(ObserverMethod<?> observer)
  {
    BaseType observedType = _cdiManager.createTargetBaseType(observer.getObservedType());
    Set<Annotation> qualifierSet = observer.getObservedQualifiers();

    Annotation[] qualifiers = new Annotation[qualifierSet.size()];
    int i = 0;
    for (Annotation qualifier : qualifierSet) {
      qualifiers[i++] = qualifier;
    }

    addObserver(observer, observedType, qualifiers);
  }

  /**
   * Registers an event observer
   *
   * @param observer the observer object
   * @param bindings the binding set for the event
   */
  public void addObserver(ObserverMethod<?> observer,
                          Type type,
                          Annotation... bindings)
  {
    BaseType eventType = _cdiManager.createTargetBaseType(type);

    addObserver(observer, eventType, bindings);
  }

  /**
   * Registers an event observer
   *
   * @param observer the observer object
   * @param bindings the binding set for the event
   */
  public void addObserver(ObserverMethod<?> observer,
                          BaseType eventBaseType,
                          Annotation... bindings)
  {
    Class<?> eventType = eventBaseType.getRawClass();

    _cdiManager.checkActive();
   
    /*
    if (eventType.getTypeParameters() != null
        && eventType.getTypeParameters().length > 0) {
      throw new IllegalArgumentException(L.l("'{0}' is an invalid event type because it's a parameterized type.",
                                             eventType));
    }
    */

    ObserverMap map = _observerMap.get(eventType);

    if (map == null) {
      map = new ObserverMap(eventType);
      ObserverMap oldMap = _observerMap.putIfAbsent(eventType, map);
       
      if (oldMap != null)
        map = oldMap;
    }

    map.addObserver(observer, eventBaseType, bindings);

    _observerMethodCache.clear();
  }

  /**
   * Registers an event observer
   *
   * @param observer the observer object
   * @param bindings the binding set for the event
   */
  public void addExtensionObserver(ObserverMethod<?> observer,
                            BaseType eventBaseType,
                            Annotation... bindings)
  {
    addObserver(_extObserverMap, observer, eventBaseType, bindings);
  }

  /**
   * Registers an event observer
   *
   * @param observer the observer object
   * @param bindings the binding set for the event
   */
  private void addObserver(ConcurrentHashMap<Class<?>,ObserverMap> observerMap,
                           ObserverMethod<?> observer,
                           BaseType eventBaseType,
                           Annotation... bindings)
  {
    Class<?> eventType = eventBaseType.getRawClass();

    _cdiManager.checkActive();

    /*
    if (eventType.getTypeParameters() != null
        && eventType.getTypeParameters().length > 0) {
      throw new IllegalArgumentException(L.l("'{0}' is an invalid event type because it's a parameterized type.",
                                             eventType));
    }
    */

    ObserverMap map = observerMap.get(eventType);

    if (map == null) {
      map = new ObserverMap(eventType);
     
      ObserverMap oldMap;
     
      oldMap = observerMap.putIfAbsent(eventType, map);
     
      if (oldMap != null)
        map = oldMap;
    }

    map.addObserver(observer, eventBaseType, bindings);

    _observerMethodCache.clear();
  }

  /**
   * Removes an event observer
   *
   * @param observer the observer object
   * @param eventType the type of event to listen for
   * @param bindings the binding set for the event
   */
  public void removeObserver(ObserverMethod<?> observer)
  {
    throw new UnsupportedOperationException(getClass().getName());
  }

  /**
   * Registers an event observer
   *
   * @param observerMethod the observer method
   */
  /*
  public void addObserver(ObserverMethod<?,?> observerMethod)
  {
    throw new UnsupportedOperationException(getClass().getName());
  }
  */
 
  public String toString()
  {
    return getClass().getSimpleName() + "[" + _cdiManager + "]";
  }
 
  static class EventKey {
    private Class<?> _type;
    private Annotation []_qualifiers;
   
    EventKey(Class<?> type, Annotation []qualifiers)
    {
      _type = type;
      _qualifiers = qualifiers;
    }

    @Override
    public int hashCode()
    {
      int hash = _type.hashCode();
     
      if (_qualifiers == null)
        return hash;
     
      for (Annotation ann : _qualifiers) {
        hash += 65521 * ann.hashCode();
      }
     
      return hash;
    }

    @Override
    public boolean equals(Object o)
    {
      if (! (o instanceof EventKey))
        return false;
     
      EventKey key = (EventKey) o;
     
      if (! _type.equals(key._type))
        return false;
     
      if (_qualifiers.length != key._qualifiers.length)
        return false;
     
      for (int i = 0; i < _qualifiers.length; i++) {
        if (_qualifiers[i] != key._qualifiers[i])
          return false;
      }
     
      return true;
    }
  }
}
TOP

Related Classes of com.caucho.config.event.EventManager$EventKey

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.