com.caucho.config.event.EventManager Maven / Gradle / Ivy
/*
* Copyright (c) 1998-2018 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.Map;
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 Map,ObserverMap> _extObserverMap
= new ConcurrentHashMap,ObserverMap>();
private ConcurrentHashMap,ObserverMap> _observerMap
= new ConcurrentHashMap,ObserverMap>();
private Map>> _observerMethodCache
= new ConcurrentHashMap>>();
public EventManager(InjectManager cdiManager)
{
_cdiManager = cdiManager;
}
public void addObserver(Bean bean, AnnotatedMethod 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 bindingSet = new HashSet();
List> 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 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 int findObserverAnnotation(AnnotatedMethod method)
{
List> 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 Set>
resolveObserverMethods(T event, Annotation... qualifiers)
{
Class> eventClass = event.getClass();
EventKey key = new EventKey(eventClass, qualifiers);
Set> 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>();
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> 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(Map,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(Map,ObserverMap> localMap,
Object event, BaseType eventType,
Annotation... qualifiers)
{
Set> observerList = new LinkedHashSet>();
fillLocalObserverList(localMap, observerList, eventType, qualifiers);
for (ObserverMethod> method : observerList) {
((ObserverMethod) method).notify(event);
}
}
private void fillLocalObserverList(Map,ObserverMap> localMap,
Set> 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 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(Map,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;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy