Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WELD-1816 Speed up construction of simple beans #793

Merged
merged 5 commits into from
Dec 15, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 30 additions & 1 deletion impl/src/main/java/org/jboss/weld/bean/ManagedBean.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,18 @@
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanAttributes;
import javax.enterprise.inject.spi.Decorator;
import javax.enterprise.inject.spi.InjectionTarget;

import org.jboss.weld.annotated.enhanced.EnhancedAnnotatedField;
import org.jboss.weld.annotated.enhanced.EnhancedAnnotatedType;
import org.jboss.weld.bootstrap.BeanDeployerEnvironment;
import org.jboss.weld.context.CreationalContextImpl;
import org.jboss.weld.context.RequestContext;
import org.jboss.weld.context.unbound.UnboundLiteral;
import org.jboss.weld.injection.producer.BasicInjectionTarget;
import org.jboss.weld.interceptor.spi.metadata.InterceptorClassMetadata;
import org.jboss.weld.interceptor.spi.model.InterceptionModel;
import org.jboss.weld.interceptor.spi.model.InterceptionType;
import org.jboss.weld.logging.BeanLogger;
import org.jboss.weld.manager.BeanManagerImpl;
import org.jboss.weld.serialization.spi.BeanIdentifier;
Expand All @@ -58,6 +62,11 @@ public class ManagedBean<T> extends AbstractClassBean<T> {

private boolean passivationCapableBean;
private boolean passivationCapableDependency;
/*
* tracks whether this bean has a @PostConstruct callbacks
* if it does not, we can skip activating/deactivating @RequestScoped context during creation
*/
private boolean hasPostConstructCallback;

/**
* Creates a simple, annotation defined Web Bean
Expand Down Expand Up @@ -149,7 +158,7 @@ public T create(CreationalContext<T> creationalContext) {
T instance = getProducer().produce(creationalContext);
getProducer().inject(instance, creationalContext);

if (beanManager.isContextActive(RequestScoped.class)) {
if (!hasPostConstructCallback || beanManager.isContextActive(RequestScoped.class)) {
getProducer().postConstruct(instance);
} else {
/*
Expand Down Expand Up @@ -264,4 +273,24 @@ private RequestContext getUnboundRequestContext() {
final CreationalContext<?> ctx = beanManager.createCreationalContext(bean);
return (RequestContext) beanManager.getReference(bean, RequestContext.class, ctx);
}

@Override
public void setProducer(InjectionTarget<T> producer) {
super.setProducer(producer);
this.hasPostConstructCallback = initHasPostConstructCallback(producer);
}

private boolean initHasPostConstructCallback(InjectionTarget<T> producer) {
if (producer instanceof BasicInjectionTarget<?>) {
BasicInjectionTarget<?> weldProducer = (BasicInjectionTarget<?>) producer;
final InterceptionModel interceptors = getInterceptors();
if (interceptors == null || interceptors.getInterceptors(InterceptionType.POST_CONSTRUCT, null).isEmpty()) {
if (!weldProducer.getLifecycleCallbackInvoker().hasPostConstructMethods()) {
return false;
}
}
}
// otherwise we assume there is a post construct callback, just to be safe
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,31 @@ public void setOuterDecorator(Object outerDecorator) {

@Override
public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
return invoke(null, self, thisMethod, proceed, args);
throw new UnsupportedOperationException();
}

@Override
public Object invoke(Stack stack, Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
if (stack == null) {
/*
* This is a lifecycle callback invocation.
* 1) Lifecycle callback interception is never suppressed by IDC
* 2) Interception of methods called by lifecycle callbacks is suppressed
*/
stack = InterceptionDecorationContext.getStack();
return invoke(stack, self, thisMethod, proceed, args, true, stack.startIfNotOnTop(this));
} else {
/*
* This is an around-invoke interception
* Interceptors are invoked as long as the current IDC is not suppressed.
*/
boolean intercept = stack.startIfNotOnTop(this);
return invoke(stack, self, thisMethod, proceed, args, intercept, intercept);
}
if (stack.startIfNotOnTop(this)) {
}

public Object invoke(Stack stack, Object self, Method thisMethod, Method proceed, Object[] args, boolean intercept, boolean popStack) throws Throwable {
if (intercept) {
try {
if (interceptorMethodHandler != null) {
if (proceed != null) {
Expand All @@ -74,7 +90,9 @@ public Object invoke(Stack stack, Object self, Method thisMethod, Method proceed
}
}
} finally {
stack.end();
if (popStack) {
stack.end();
}
}
}
SecurityActions.ensureAccessible(proceed);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class InterceptionDecorationContext {
private static ThreadLocal<Stack> interceptionContexts = new ThreadLocal<Stack>();

public static class Stack implements RequestScopedItem {
private final boolean removeWhenEmpty;
private boolean removeWhenEmpty;
private final Deque<CombinedInterceptorAndDecoratorStackMethodHandler> elements;
private final ThreadLocal<Stack> interceptionContexts;
private boolean valid;
Expand All @@ -60,10 +60,6 @@ private Stack(ThreadLocal<Stack> interceptionContexts) {
this.valid = true;
}

private boolean shouldRemove() {
return removeWhenEmpty && elements.isEmpty();
}

/**
* Pushes the given context to the stack if the given context is not on top of the stack already.
* If push happens, the caller is responsible for calling {@link #endInterceptorContext()} after the invocation finishes.
Expand Down Expand Up @@ -96,9 +92,7 @@ public CombinedInterceptorAndDecoratorStackMethodHandler peek() {
private CombinedInterceptorAndDecoratorStackMethodHandler pop() {
checkState();
CombinedInterceptorAndDecoratorStackMethodHandler top = elements.removeFirst();
if (shouldRemove()) {
invalidate();
}
removeIfEmpty();
return top;
}

Expand All @@ -110,8 +104,21 @@ private void checkState() {

@Override
public void invalidate() {
interceptionContexts.remove();
valid = false;
/*
* This cached item is being invalidated.
* It does not necessarily mean that the request is being destroyed - it may just be the case that it is being flushed in the middle
* of a request (e.g. for AlterableContext.destroy()).
* Therefore, we cannot remove IDC now but we just set removeWhenEmpty flag and let it remove itself once the stack gets empty.
*/
removeWhenEmpty = true;
removeIfEmpty();
}

private void removeIfEmpty() {
if (removeWhenEmpty && elements.isEmpty()) {
interceptionContexts.remove();
valid = false;
}
}

public int size() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ void startIfNotOnTop(CodeAttribute b, ClassMethod method) {

// if handler != null (may happen inside constructor calls)
final BranchEnd handlerNull = b.ifnull();
b.checkcast(CombinedInterceptorAndDecoratorStackMethodHandler.class.getName());
b.invokestatic(INTERCEPTION_DECORATION_CONTEXT_CLASS_NAME, START_INTERCEPTOR_CONTEXT_IF_NOT_ON_TOP_METHOD_NAME,
START_INTERCEPTOR_CONTEXT_IF_NOT_ON_TOP_METHOD_SIGNATURE);
final BranchEnd endOfIfStatement = b.gotoInstruction();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public Object[] getParameterValues(BeanManagerImpl manager, CreationalContext<?>
Iterator<ParameterInjectionPoint<?, T>> iterator = getParameterInjectionPoints().iterator();
for (int i = 0; i < parameterValues.length; i++) {
ParameterInjectionPoint<?, ?> param = iterator.next();
if (param.getAnnotated().isAnnotationPresent(TransientReference.class)) {
if (hasTransientReferenceParameter && param.getAnnotated().isAnnotationPresent(TransientReference.class)) {
parameterValues[i] = param.getValueToInject(manager, transientReference);
} else {
parameterValues[i] = param.getValueToInject(manager, ctx);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ protected Object[] getParameterValues(Object specialVal, BeanManagerImpl manager
ParameterInjectionPoint<?, ?> param = iterator.next();
if (i == specialInjectionPointIndex) {
parameterValues[i] = specialVal;
} else if (param.getAnnotated().isAnnotationPresent(TransientReference.class)) {
} else if (hasTransientReferenceParameter && param.getAnnotated().isAnnotationPresent(TransientReference.class)) {
parameterValues[i] = param.getValueToInject(manager, transientReferenceContext);
} else {
parameterValues[i] = param.getValueToInject(manager, ctx);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import javax.decorator.Delegate;
import javax.enterprise.inject.spi.Bean;

import org.jboss.weld.annotated.enhanced.EnhancedAnnotated;
import org.jboss.weld.serialization.BeanHolder;
import org.jboss.weld.util.reflection.HierarchyDiscovery;
import org.jboss.weld.util.reflection.Reflections;
Expand All @@ -38,15 +39,17 @@ public abstract class AbstractInferringInjectionPointAttributes<T, S> implements
private final BeanHolder<?> bean;
private final Set<Annotation> qualifiers;
private final TypeAttribute typeAttribute;
private final boolean delegate;

public AbstractInferringInjectionPointAttributes(String contextId, Bean<?> bean, Set<Annotation> qualifiers, Class<?> declaringComponentClass) {
public AbstractInferringInjectionPointAttributes(EnhancedAnnotated<?, ?> annotatedElement, String contextId, Bean<?> bean, Set<Annotation> qualifiers, Class<?> declaringComponentClass) {
this.bean = BeanHolder.of(contextId, bean);
this.qualifiers = qualifiers;
if (bean == null) {
this.typeAttribute = new NonContextualInjectionPointTypeAttribute(declaringComponentClass);
} else {
this.typeAttribute = new BeanInjectionPointTypeAttribute();
}
this.delegate = annotatedElement.isAnnotationPresent(Delegate.class);
}

@Override
Expand All @@ -66,7 +69,7 @@ public Bean<?> getBean() {

@Override
public boolean isDelegate() {
return getAnnotated().isAnnotationPresent(Delegate.class);
return delegate;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public static <T, X> InferringFieldInjectionPointAttributes<T, X> of(EnhancedAnn
private final AnnotatedField<X> field;

protected InferringFieldInjectionPointAttributes(EnhancedAnnotatedField<T, X> field, Bean<?> bean, Class<?> declaringComponentClass, BeanManagerImpl manager) {
super(manager.getContextId(), bean, SharedObjectCache.instance(manager).getSharedSet(field.getQualifiers()), declaringComponentClass);
super(field, manager.getContextId(), bean, SharedObjectCache.instance(manager).getSharedSet(field.getQualifiers()), declaringComponentClass);
this.field = field.slim();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public static <T, X> InferringParameterInjectionPointAttributes<T, X> of(Enhance
private final AnnotatedParameter<X> parameter;

protected InferringParameterInjectionPointAttributes(EnhancedAnnotatedParameter<T, X> parameter, Bean<?> bean, Class<?> declaringComponentClass, BeanManagerImpl manager) {
super(manager.getContextId(), bean, SharedObjectCache.instance(manager).getSharedSet(parameter.getQualifiers()), declaringComponentClass);
super(parameter, manager.getContextId(), bean, SharedObjectCache.instance(manager).getSharedSet(parameter.getQualifiers()), declaringComponentClass);
this.parameter = parameter.slim();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import org.jboss.weld.bean.CustomDecoratorWrapper;
import org.jboss.weld.bean.DecoratorImpl;
import org.jboss.weld.interceptor.spi.model.InterceptionModel;
import org.jboss.weld.interceptor.util.InterceptionUtils;
import org.jboss.weld.logging.BeanLogger;
import org.jboss.weld.manager.BeanManagerImpl;
import org.jboss.weld.resources.ClassTransformer;
Expand Down Expand Up @@ -62,24 +61,6 @@ public BeanInjectionTarget(EnhancedAnnotatedType<T> type, Bean<T> bean, BeanMana
this(type, bean, beanManager, ResourceInjector.of(type, bean, beanManager), DefaultLifecycleCallbackInvoker.of(type));
}

@Override
public void postConstruct(T instance) {
if (getInstantiator().hasInterceptorSupport()) {
InterceptionUtils.executePostConstruct(instance);
} else {
super.postConstruct(instance);
}
}

@Override
public void preDestroy(T instance) {
if (getInstantiator().hasInterceptorSupport()) {
InterceptionUtils.executePredestroy(instance);
} else {
super.preDestroy(instance);
}
}

@Override
public void dispose(T instance) {
// No-op
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ protected Object executeInterception(Object instance, Method method, Method proc
return Reflections.invokeAndUnwrap(instance, proceed, args);
}
} else {
return new WeldInvocationContext(instance, method, proceed, args, chain, stack.peek()).proceed();
return new WeldInvocationContext(instance, method, proceed, args, chain, stack).proceed();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ public WeldInvocationContext(Constructor<?> constructor, Object[] parameters, Ma
this(new SimpleInvocationContext(constructor, parameters, contextData), chain, null);
}

public WeldInvocationContext(Object target, Method targetMethod, Method proceed, Object[] parameters, List<InterceptorMethodInvocation> chain, CombinedInterceptorAndDecoratorStackMethodHandler currentHandler) {
this(new SimpleInvocationContext(target, targetMethod, proceed, parameters), chain, currentHandler);
public WeldInvocationContext(Object target, Method targetMethod, Method proceed, Object[] parameters, List<InterceptorMethodInvocation> chain, Stack stack) {
this(new SimpleInvocationContext(target, targetMethod, proceed, parameters), chain, (stack == null) ? null : stack.peek());
}

public WeldInvocationContext(InvocationContext delegate, List<InterceptorMethodInvocation> chain) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2014, 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.tests.interceptors.cache;

import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class Bar {

public void bar() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2014, 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.tests.interceptors.cache;

import javax.inject.Inject;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.BeanArchive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.weld.bean.proxy.InterceptionDecorationContext;
import org.jboss.weld.context.cache.RequestScopedCache;
import org.junit.Test;
import org.junit.runner.RunWith;

/**
* Tests that {@link InterceptionDecorationContext} survives {@link RequestScopedCache} being flushed.
*
* https://issues.jboss.org/browse/WELD-1812
*
* @author Jozef Hartinger
*
*/
@RunWith(Arquillian.class)
public class CachedInterceptionDecorationContextTest {

@Inject
private Foo foo;

@Deployment
public static Archive<?> getDeployment() {
return ShrinkWrap.create(BeanArchive.class).addPackage(CachedInterceptionDecorationContextTest.class.getPackage());
}

@Test
public void testInvalidatedCacheDoesNotInfluenceInterception() {
foo.foo();
}
}