diff --git a/impl/src/main/java/org/jboss/weld/event/CurrentEventMetadata.java b/impl/src/main/java/org/jboss/weld/event/CurrentEventMetadata.java index 16f8f07a552..4ac2a2eebaf 100644 --- a/impl/src/main/java/org/jboss/weld/event/CurrentEventMetadata.java +++ b/impl/src/main/java/org/jboss/weld/event/CurrentEventMetadata.java @@ -16,7 +16,9 @@ */ package org.jboss.weld.event; +import javax.enterprise.inject.spi.EventMetadata; + import org.jboss.weld.injection.ThreadLocalStackService; -public class CurrentEventMetadata extends ThreadLocalStackService> { +public class CurrentEventMetadata extends ThreadLocalStackService { } diff --git a/impl/src/main/java/org/jboss/weld/event/FastEvent.java b/impl/src/main/java/org/jboss/weld/event/FastEvent.java new file mode 100644 index 00000000000..3878dd98070 --- /dev/null +++ b/impl/src/main/java/org/jboss/weld/event/FastEvent.java @@ -0,0 +1,181 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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.jboss.weld.event; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Set; + +import javax.enterprise.event.Event; +import javax.enterprise.inject.Instance; +import javax.enterprise.inject.spi.EventMetadata; +import javax.enterprise.inject.spi.InjectionPoint; +import javax.enterprise.inject.spi.ObserverMethod; +import javax.enterprise.util.TypeLiteral; + +import org.jboss.weld.injection.attributes.WeldInjectionPointAttributes; +import org.jboss.weld.manager.BeanManagerImpl; + +import com.google.common.collect.ImmutableSet; + +/** + * An optimized internal facility for dispatching events. + * + *

+ * FastEvent eliminates most of the overhead associated with request dispatching by resolving observer methods upfront. It is therefore suitable for cases when + * certain event is dispatched repeatedly. A FastEvent instance should be created once for a given event type / qualifiers combination and then reused every + * time a given event is dispatched. + *

+ * + *

+ * FastEvent provides a subset of functionality provided by {@link Event} and these additional constraints apply to its usage: + *

+ * + * + * + *

+ * These constraints should always be carefully considered when deciding whether to use FastEvent or not. FastEvent is an internal construct and should + * not be used by an application. + *

+ * + * @author Jozef Hartinger + * + * @param event type + */ +public class FastEvent { + + @SuppressWarnings("serial") + private static final Type EVENT_METADATA_INSTANCE_TYPE = new TypeLiteral>() { + }.getType(); + + /** + * Determines whether any of the resolved observer methods is either extension-provided or contains an injection point with {@link EventMetadata} type. + */ + private static boolean isMetadataRequired(Set> resolvedObserverMethods) { + for (ObserverMethod observer : resolvedObserverMethods) { + if (observer instanceof ObserverMethodImpl) { + ObserverMethodImpl observerImpl = (ObserverMethodImpl) observer; + for (WeldInjectionPointAttributes ip : observerImpl.getInjectionPoints()) { + Type type = ip.getType(); + if (EventMetadata.class.equals(type) || EVENT_METADATA_INSTANCE_TYPE.equals(type)) { + return true; + } + } + } else { + return true; + } + } + return false; + } + + /** + * Same as {@link #of(Class, BeanManagerImpl, Annotation...)}, just the accessible lenient observer notifier is used for observer method resolution + */ + public static FastEvent of(Class type, BeanManagerImpl manager, Annotation... qualifiers) { + return of(type, manager, manager.getAccessibleLenientObserverNotifier(), qualifiers); + } + + /** + * Constructs a new FastEvent instance + * @param type the event type + * @param manager the bean manager + * @param notifier the notifier to be used for observer method resolution + * @param qualifiers the event qualifiers + * @return + */ + public static FastEvent of(Class type, BeanManagerImpl manager, ObserverNotifier notifier, Annotation... qualifiers) { + Set> resolvedObserverMethods = notifier. resolveObserverMethods(notifier.buildEventResolvable(type, qualifiers)); + if (isMetadataRequired(resolvedObserverMethods)) { + EventMetadata metadata = new EventMetadataImpl(type, qualifiers); + CurrentEventMetadata metadataService = manager.getServices().get(CurrentEventMetadata.class); + return new FastEventWithMetadataPropagation(resolvedObserverMethods, metadata, metadataService); + } else { + return new FastEvent(resolvedObserverMethods); + } + } + + private final Set> resolvedObserverMethods; + + private FastEvent(Set> resolvedObserverMethods) { + this.resolvedObserverMethods = resolvedObserverMethods; + } + + public void fire(T event) { + for (ObserverMethod observer : resolvedObserverMethods) { + observer.notify(event); + } + } + + private static class FastEventWithMetadataPropagation extends FastEvent { + + private final EventMetadata metadata; + private final CurrentEventMetadata metadataService; + + private FastEventWithMetadataPropagation(Set> resolvedObserverMethods, EventMetadata metadata, + CurrentEventMetadata metadataService) { + super(resolvedObserverMethods); + this.metadata = metadata; + this.metadataService = metadataService; + } + + @Override + public void fire(T event) { + if (metadata != null) { + metadataService.push(metadata); + } + try { + super.fire(event); + } finally { + if (metadata != null) { + metadataService.pop(); + } + } + } + } + + private static class EventMetadataImpl implements EventMetadata { + + private final Set qualifiers; + private final Type type; + + private EventMetadataImpl(Type type, Annotation... qualifiers) { + this.type = type; + this.qualifiers = ImmutableSet.copyOf(qualifiers); + } + + @Override + public Set getQualifiers() { + return qualifiers; + } + + @Override + public InjectionPoint getInjectionPoint() { + return null; + } + + @Override + public Type getType() { + return type; + } + } +} diff --git a/impl/src/main/java/org/jboss/weld/servlet/ConversationContextActivator.java b/impl/src/main/java/org/jboss/weld/servlet/ConversationContextActivator.java index 3e8be322f47..28a5a5a4fa6 100644 --- a/impl/src/main/java/org/jboss/weld/servlet/ConversationContextActivator.java +++ b/impl/src/main/java/org/jboss/weld/servlet/ConversationContextActivator.java @@ -24,6 +24,7 @@ import org.jboss.weld.context.http.HttpConversationContext; import org.jboss.weld.context.http.HttpRequestContext; import org.jboss.weld.context.http.HttpRequestContextImpl; +import org.jboss.weld.event.FastEvent; import org.jboss.weld.literal.DestroyedLiteral; import org.jboss.weld.literal.InitializedLiteral; import org.jboss.weld.logging.ConversationLogger; @@ -50,8 +51,13 @@ public class ConversationContextActivator { private HttpConversationContext httpConversationContextCache; private HttpRequestContext requestContextCache; + private final FastEvent conversationInitializedEvent; + private final FastEvent conversationDestroyedEvent; + protected ConversationContextActivator(BeanManagerImpl beanManager) { this.beanManager = beanManager; + conversationInitializedEvent = FastEvent.of(HttpServletRequest.class, beanManager, InitializedLiteral.CONVERSATION); + conversationDestroyedEvent = FastEvent.of(HttpServletRequest.class, beanManager, DestroyedLiteral.CONVERSATION); } private HttpConversationContext httpConversationContext() { @@ -92,7 +98,7 @@ protected void activateConversationContext(HttpServletRequest request) { setContextActivatedInRequest(request); conversationContext.activate(cid); if (cid == null) { // transient conversation - beanManager.getAccessibleLenientObserverNotifier().fireEvent(request, InitializedLiteral.CONVERSATION); + conversationInitializedEvent.fire(request); } } else { /* @@ -156,7 +162,7 @@ protected void deactivateConversationContext(HttpServletRequest request) { conversationContext.invalidate(); conversationContext.deactivate(); if (isTransient) { - beanManager.getAccessibleLenientObserverNotifier().fireEvent(request, DestroyedLiteral.CONVERSATION); + conversationDestroyedEvent.fire(request); } } } diff --git a/impl/src/main/java/org/jboss/weld/servlet/HttpContextLifecycle.java b/impl/src/main/java/org/jboss/weld/servlet/HttpContextLifecycle.java index 174f49ef133..23c217fe22f 100644 --- a/impl/src/main/java/org/jboss/weld/servlet/HttpContextLifecycle.java +++ b/impl/src/main/java/org/jboss/weld/servlet/HttpContextLifecycle.java @@ -28,6 +28,7 @@ import org.jboss.weld.context.http.HttpRequestContextImpl; import org.jboss.weld.context.http.HttpSessionContext; import org.jboss.weld.context.http.HttpSessionDestructionContext; +import org.jboss.weld.event.FastEvent; import org.jboss.weld.literal.DestroyedLiteral; import org.jboss.weld.literal.InitializedLiteral; import org.jboss.weld.logging.ServletLogger; @@ -59,11 +60,24 @@ public class HttpContextLifecycle implements Service { private final ConversationContextActivator conversationContextActivator; private final HttpContextActivationFilter contextActivationFilter; + private final FastEvent applicationInitializedEvent; + private final FastEvent applicationDestroyedEvent; + private final FastEvent requestInitializedEvent; + private final FastEvent requestDestroyedEvent; + private final FastEvent sessionInitializedEvent; + private final FastEvent sessionDestroyedEvent; + public HttpContextLifecycle(BeanManagerImpl beanManager, HttpContextActivationFilter contextActivationFilter) { this.beanManager = beanManager; this.conversationContextActivator = new ConversationContextActivator(beanManager); this.conversationActivationEnabled = null; this.contextActivationFilter = contextActivationFilter; + this.applicationInitializedEvent = FastEvent.of(ServletContext.class, beanManager, InitializedLiteral.APPLICATION); + this.applicationDestroyedEvent = FastEvent.of(ServletContext.class, beanManager, DestroyedLiteral.APPLICATION); + this.requestInitializedEvent = FastEvent.of(HttpServletRequest.class, beanManager, InitializedLiteral.REQUEST); + this.requestDestroyedEvent = FastEvent.of(HttpServletRequest.class, beanManager, DestroyedLiteral.REQUEST); + this.sessionInitializedEvent = FastEvent.of(HttpSession.class, beanManager, InitializedLiteral.SESSION); + this.sessionDestroyedEvent = FastEvent.of(HttpSession.class, beanManager, DestroyedLiteral.SESSION); } private HttpSessionDestructionContext getSessionDestructionContext() { @@ -88,17 +102,17 @@ public HttpRequestContext getRequestContext() { } public void contextInitialized(ServletContext ctx) { - beanManager.getAccessibleLenientObserverNotifier().fireEvent(ctx, InitializedLiteral.APPLICATION); + applicationInitializedEvent.fire(ctx); } public void contextDestroyed(ServletContext ctx) { - beanManager.getAccessibleLenientObserverNotifier().fireEvent(ctx, DestroyedLiteral.APPLICATION); + applicationDestroyedEvent.fire(ctx); } public void sessionCreated(HttpSession session) { SessionHolder.sessionCreated(session); conversationContextActivator.sessionCreated(session); - beanManager.getAccessibleLenientObserverNotifier().fireEvent(session, InitializedLiteral.SESSION); + sessionInitializedEvent.fire(session); } public void sessionDestroyed(HttpSession session) { @@ -111,7 +125,7 @@ public void sessionDestroyed(HttpSession session) { if (destroyed) { // we are outside of a request (the session timed out) and therefore the session was destroyed immediately // we can fire the @Destroyed(SessionScoped.class) event immediately - beanManager.getAccessibleLenientObserverNotifier().fireEvent(session, DestroyedLiteral.SESSION); + sessionDestroyedEvent.fire(session); } else { // the old session won't be available at the time we destroy this request // let's store its reference until then @@ -158,7 +172,7 @@ public void requestInitialized(HttpServletRequest request, ServletContext ctx) { if (conversationActivationEnabled) { conversationContextActivator.activateConversationContext(request); } - beanManager.getAccessibleLenientObserverNotifier().fireEvent(request, InitializedLiteral.REQUEST); + requestInitializedEvent.fire(request); } catch (RuntimeException e) { try { requestDestroyed(request); @@ -188,12 +202,11 @@ public void requestDestroyed(HttpServletRequest request) { getRequestContext().invalidate(); getRequestContext().deactivate(); // fire @Destroyed(RequestScoped.class) - beanManager.getAccessibleLenientObserverNotifier().fireEvent(request, DestroyedLiteral.REQUEST); + requestDestroyedEvent.fire(request); getSessionContext().deactivate(); // fire @Destroyed(SessionScoped.class) if (!getSessionContext().isValid()) { - beanManager.getAccessibleLenientObserverNotifier().fireEvent(request.getAttribute(HTTP_SESSION), - DestroyedLiteral.SESSION); + sessionDestroyedEvent.fire((HttpSession) request.getAttribute(HTTP_SESSION)); } } finally { getRequestContext().dissociate(request);