Skip to content

Commit

Permalink
WELD-1496 Optimize context activation/deactivation events
Browse files Browse the repository at this point in the history
  • Loading branch information
jharting committed Sep 20, 2013
1 parent 28833cf commit 4eff689
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 11 deletions.
Expand Up @@ -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<EventPacket<?>> {
public class CurrentEventMetadata extends ThreadLocalStackService<EventMetadata> {
}
181 changes: 181 additions & 0 deletions 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.
*
* <p>
* 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.
* </p>
*
* <p>
* FastEvent provides a subset of functionality provided by {@link Event} and these additional constraints apply to its usage:
* </p>
*
* <ul>
* <li>Event type and qualifiers must be known at FastEvent construction time. The actual type of the event object passed to the {@link #fire(Object)} method is
* not considered for observer method resolution.</li>
* <li>Events dispatched using FastEvent are always delivered immediately. If an observer method is transactional it will be notified immediately and not during
* the matching transaction phase.</li>
* <li>FastEvent is not serializable</li>
* </ul>
*
* <p>
* These constraints should always be carefully considered when deciding whether to use FastEvent or not. FastEvent is an internal construct and <strong>should
* not</strong> be used by an application.
* </p>
*
* @author Jozef Hartinger
*
* @param <T> event type
*/
public class FastEvent<T> {

@SuppressWarnings("serial")
private static final Type EVENT_METADATA_INSTANCE_TYPE = new TypeLiteral<Instance<EventMetadata>>() {
}.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<? extends ObserverMethod<?>> 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 <T> FastEvent<T> of(Class<T> 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 <T> FastEvent<T> of(Class<T> type, BeanManagerImpl manager, ObserverNotifier notifier, Annotation... qualifiers) {
Set<ObserverMethod<? super T>> resolvedObserverMethods = notifier.<T> resolveObserverMethods(notifier.buildEventResolvable(type, qualifiers));
if (isMetadataRequired(resolvedObserverMethods)) {
EventMetadata metadata = new EventMetadataImpl(type, qualifiers);
CurrentEventMetadata metadataService = manager.getServices().get(CurrentEventMetadata.class);
return new FastEventWithMetadataPropagation<T>(resolvedObserverMethods, metadata, metadataService);
} else {
return new FastEvent<T>(resolvedObserverMethods);
}
}

private final Set<ObserverMethod<? super T>> resolvedObserverMethods;

private FastEvent(Set<ObserverMethod<? super T>> resolvedObserverMethods) {
this.resolvedObserverMethods = resolvedObserverMethods;
}

public void fire(T event) {
for (ObserverMethod<? super T> observer : resolvedObserverMethods) {
observer.notify(event);
}
}

private static class FastEventWithMetadataPropagation<T> extends FastEvent<T> {

private final EventMetadata metadata;
private final CurrentEventMetadata metadataService;

private FastEventWithMetadataPropagation(Set<ObserverMethod<? super T>> 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<Annotation> qualifiers;
private final Type type;

private EventMetadataImpl(Type type, Annotation... qualifiers) {
this.type = type;
this.qualifiers = ImmutableSet.copyOf(qualifiers);
}

@Override
public Set<Annotation> getQualifiers() {
return qualifiers;
}

@Override
public InjectionPoint getInjectionPoint() {
return null;
}

@Override
public Type getType() {
return type;
}
}
}
Expand Up @@ -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;
Expand All @@ -50,8 +51,13 @@ public class ConversationContextActivator {
private HttpConversationContext httpConversationContextCache;
private HttpRequestContext requestContextCache;

private final FastEvent<HttpServletRequest> conversationInitializedEvent;
private final FastEvent<HttpServletRequest> 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() {
Expand Down Expand Up @@ -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 {
/*
Expand Down Expand Up @@ -156,7 +162,7 @@ protected void deactivateConversationContext(HttpServletRequest request) {
conversationContext.invalidate();
conversationContext.deactivate();
if (isTransient) {
beanManager.getAccessibleLenientObserverNotifier().fireEvent(request, DestroyedLiteral.CONVERSATION);
conversationDestroyedEvent.fire(request);
}
}
}
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -59,11 +60,24 @@ public class HttpContextLifecycle implements Service {
private final ConversationContextActivator conversationContextActivator;
private final HttpContextActivationFilter contextActivationFilter;

private final FastEvent<ServletContext> applicationInitializedEvent;
private final FastEvent<ServletContext> applicationDestroyedEvent;
private final FastEvent<HttpServletRequest> requestInitializedEvent;
private final FastEvent<HttpServletRequest> requestDestroyedEvent;
private final FastEvent<HttpSession> sessionInitializedEvent;
private final FastEvent<HttpSession> 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() {
Expand All @@ -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) {
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 4eff689

Please sign in to comment.