Skip to content

Commit

Permalink
Make sure WeldInvocationContext survives being dispatched to a differ…
Browse files Browse the repository at this point in the history
…ent thread
  • Loading branch information
jharting committed Nov 14, 2014
1 parent c1305ff commit cd91081
Show file tree
Hide file tree
Showing 11 changed files with 309 additions and 33 deletions.
Expand Up @@ -167,25 +167,30 @@ public static void endInterceptorContext() {
* at the time the proxy is called (meaning the caller is not intercepted), there is no need to create new interception context. This is an optimization as the
* first startInterceptorContext call is expensive.
*
* The caller of this method is required to call {@link #endInterceptorContext()} if and only if this method returns true.
* If this method returns a non-null value, the caller of this method is required to call {@link Stack#end()} on the returned value.
*/
public static boolean startIfNotEmpty() {
public static Stack startIfNotEmpty() {
Stack stack = interceptionContexts.get();
if (empty(stack)) {
return false;
return null;
}
stack.push(CombinedInterceptorAndDecoratorStackMethodHandler.NULL_INSTANCE);
return true;
return stack;
}

/**
* 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.
* If this method return a non-null value, the caller is responsible for calling {@link #endInterceptorContext()}
* after the invocation finishes.
* @param context the given context
* @return true if the given context was pushed to the top of the stack, false if the given context was on top already
*/
public static boolean startIfNotOnTop(CombinedInterceptorAndDecoratorStackMethodHandler context) {
return getStack().startIfNotOnTop(context);
public static Stack startIfNotOnTop(CombinedInterceptorAndDecoratorStackMethodHandler context) {
Stack stack = getStack();
if (stack.startIfNotOnTop(context)) {
return stack;
}
return null;
}

/**
Expand Down
Expand Up @@ -16,7 +16,6 @@
*/
package org.jboss.weld.bean.proxy;

import static org.jboss.weld.util.bytecode.DescriptorUtils.BOOLEAN_CLASS_DESCRIPTOR;
import static org.jboss.weld.util.bytecode.DescriptorUtils.DOUBLE_CLASS_DESCRIPTOR;
import static org.jboss.weld.util.bytecode.DescriptorUtils.LONG_CLASS_DESCRIPTOR;
import static org.jboss.weld.util.bytecode.DescriptorUtils.VOID_CLASS_DESCRIPTOR;
Expand All @@ -27,6 +26,7 @@
import org.jboss.classfilewriter.code.BranchEnd;
import org.jboss.classfilewriter.code.CodeAttribute;
import org.jboss.classfilewriter.code.ExceptionHandler;
import org.jboss.weld.bean.proxy.InterceptionDecorationContext.Stack;

/**
* Generates bytecode that wraps {@link #doWork(CodeAttribute, ClassMethod)} within {@link InterceptionDecorationContext#startInterceptorContextIfNotEmpty()}
Expand All @@ -41,10 +41,12 @@ abstract class RunWithinInterceptionDecorationContextGenerator {
static final String INTERCEPTION_DECORATION_CONTEXT_CLASS_NAME = InterceptionDecorationContext.class.getName();
static final String START_INTERCEPTOR_CONTEXT_IF_NOT_EMPTY_METHOD_NAME = "startIfNotEmpty";
static final String START_INTERCEPTOR_CONTEXT_IF_NOT_ON_TOP_METHOD_NAME = "startIfNotOnTop";
static final String START_INTERCEPTOR_CONTEXT_IF_NOT_ON_TOP_METHOD_SIGNATURE = getMethodDescriptor(
new String[] { classToStringRepresentation(CombinedInterceptorAndDecoratorStackMethodHandler.class) }, BOOLEAN_CLASS_DESCRIPTOR);
static final String END_INTERCEPTOR_CONTEXT_METHOD_NAME = "endInterceptorContext";
static final String END_INTERCEPTOR_CONTEXT_METHOD_NAME = "end";
private static final String STACK_DESCRIPTOR = classToStringRepresentation(Stack.class);
private static final String EMPTY_PARENTHESES = "()";
private static final String RETURNS_STACK_DESCRIPTOR = EMPTY_PARENTHESES + STACK_DESCRIPTOR;
static final String START_INTERCEPTOR_CONTEXT_IF_NOT_ON_TOP_METHOD_SIGNATURE = getMethodDescriptor(
new String[] { classToStringRepresentation(CombinedInterceptorAndDecoratorStackMethodHandler.class) }, STACK_DESCRIPTOR);

private final ClassMethod classMethod;
private final CodeAttribute b;
Expand All @@ -58,9 +60,8 @@ abstract class RunWithinInterceptionDecorationContextGenerator {
abstract void doReturn(CodeAttribute b, ClassMethod method);

void startIfNotEmpty(CodeAttribute b, ClassMethod method) {
b.invokestatic(INTERCEPTION_DECORATION_CONTEXT_CLASS_NAME, START_INTERCEPTOR_CONTEXT_IF_NOT_EMPTY_METHOD_NAME, EMPTY_PARENTHESES
+ BOOLEAN_CLASS_DESCRIPTOR);
// store the outcome some that we know later whether to end the context or not
b.invokestatic(INTERCEPTION_DECORATION_CONTEXT_CLASS_NAME, START_INTERCEPTOR_CONTEXT_IF_NOT_EMPTY_METHOD_NAME, RETURNS_STACK_DESCRIPTOR);
// store the outcome so that we know later whether to end the context or not
storeToLocalVariable(0);
}

Expand All @@ -77,8 +78,7 @@ void startIfNotOnTop(CodeAttribute b, ClassMethod method) {
final BranchEnd endOfIfStatement = b.gotoInstruction();
b.branchEnd(handlerNull);
// else started = false
b.pop(); // pop null value out of the stack
b.iconst(0);
// keeping null handler on top of stack
b.branchEnd(endOfIfStatement);

storeToLocalVariable(0);
Expand Down Expand Up @@ -114,10 +114,15 @@ void withinCatchBlock(CodeAttribute b, ClassMethod method) {
* Ends interception context if it was previously stated. This is indicated by a local variable with index 0.
*/
void endIfStarted(CodeAttribute b, ClassMethod method) {
b.iload(getLocalVariableIndex(0));
final BranchEnd conditional = b.ifeq();
b.invokestatic(INTERCEPTION_DECORATION_CONTEXT_CLASS_NAME, END_INTERCEPTOR_CONTEXT_METHOD_NAME, EMPTY_PARENTHESES + VOID_CLASS_DESCRIPTOR);
b.branchEnd(conditional);
b.aload(getLocalVariableIndex(0));
b.dup();
final BranchEnd ifnotnull = b.ifnull();
b.checkcast(Stack.class);
b.invokevirtual(Stack.class.getName(), END_INTERCEPTOR_CONTEXT_METHOD_NAME, EMPTY_PARENTHESES + VOID_CLASS_DESCRIPTOR);
BranchEnd ifnull = b.gotoInstruction();
b.branchEnd(ifnotnull);
b.pop(); // remove null Stack
b.branchEnd(ifnull);
}

/**
Expand All @@ -144,7 +149,7 @@ void runStartIfNotOnTop() {
}

void storeToLocalVariable(int i) {
b.istore(getLocalVariableIndex(0));
b.astore(getLocalVariableIndex(0));
}

/**
Expand Down
Expand Up @@ -66,7 +66,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.interceptorMethods, chain.interceptorBindings, stack).proceed();
return new WeldInvocationContext(instance, method, proceed, args, chain.interceptorMethods, chain.interceptorBindings, stack.peek()).proceed();
}
}

Expand Down
Expand Up @@ -29,6 +29,7 @@
import javax.interceptor.InvocationContext;

import org.jboss.weld.bean.proxy.CombinedInterceptorAndDecoratorStackMethodHandler;
import org.jboss.weld.bean.proxy.InterceptionDecorationContext;
import org.jboss.weld.bean.proxy.InterceptionDecorationContext.Stack;
import org.jboss.weld.experimental.ExperimentalInvocationContext;
import org.jboss.weld.logging.InterceptorLogger;
Expand All @@ -49,24 +50,22 @@ public class WeldInvocationContext extends ForwardingInvocationContext implement

private int position;
private final List<InterceptorMethodInvocation> chain;
private final CombinedInterceptorAndDecoratorStackMethodHandler interceptionContext;
private final CombinedInterceptorAndDecoratorStackMethodHandler currentHandler;
private final InvocationContext delegate;
private final Set<Annotation> interceptorBindings;
private final Stack stack;

public WeldInvocationContext(Constructor<?> constructor, Object[] parameters, Map<String, Object> contextData, List<InterceptorMethodInvocation> chain, Set<Annotation> interceptorBindings) {
this(new SimpleInvocationContext(constructor, parameters, contextData), chain, interceptorBindings, null);
}

public WeldInvocationContext(Object target, Method targetMethod, Method proceed, Object[] parameters, List<InterceptorMethodInvocation> chain, Set<Annotation> interceptorBindings, Stack stack) {
this(new SimpleInvocationContext(target, targetMethod, proceed, parameters), chain, interceptorBindings, stack);
public WeldInvocationContext(Object target, Method targetMethod, Method proceed, Object[] parameters, List<InterceptorMethodInvocation> chain, Set<Annotation> interceptorBindings, CombinedInterceptorAndDecoratorStackMethodHandler currentHandler) {
this(new SimpleInvocationContext(target, targetMethod, proceed, parameters), chain, interceptorBindings, currentHandler);
}

public WeldInvocationContext(InvocationContext delegate, List<InterceptorMethodInvocation> chain, Set<Annotation> interceptorBindings, Stack stack) {
public WeldInvocationContext(InvocationContext delegate, List<InterceptorMethodInvocation> chain, Set<Annotation> interceptorBindings, CombinedInterceptorAndDecoratorStackMethodHandler currentHandler) {
this.delegate = delegate;
this.chain = chain;
this.stack = stack;
this.interceptionContext = (stack == null) ? null : stack.peek();
this.currentHandler = currentHandler;
if (interceptorBindings == null) {
this.interceptorBindings = Collections.<Annotation>emptySet();
} else {
Expand Down Expand Up @@ -110,13 +109,14 @@ protected Object interceptorChainCompleted() throws Exception {

@Override
public Object proceed() throws Exception {
boolean pushed = false;
Stack stack = null;
/*
* No need to push the context for the first interceptor as the current context
* was set by CombinedInterceptorAndDecoratorStackMethodHandler
*/
if (stack != null && position != 0) {
pushed = stack.startIfNotOnTop(interceptionContext);

if (currentHandler != null && position != 0) {
stack = InterceptionDecorationContext.startIfNotOnTop(currentHandler);
}

try {
Expand All @@ -135,7 +135,7 @@ public Object proceed() throws Exception {
}
throw new RuntimeException(cause);
} finally {
if (pushed) {
if (stack != null) {
stack.end();
}
}
Expand Down
@@ -0,0 +1,28 @@
/*
* 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.thread;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import javax.interceptor.InterceptorBinding;

@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
public @interface DispatchToThread {

}
@@ -0,0 +1,46 @@
/*
* 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.thread;

import java.util.concurrent.Future;

import javax.annotation.Priority;
import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

/**
* Verifies that interception works fine if an interceptor dispatches the invocation to a different thread and the rest of the chain is executed there.
*
* @author Jozef Hartinger
*
*/
@Interceptor
@DispatchToThread
@Priority(Interceptor.Priority.APPLICATION)
public class DispatchToThreadInterceptor {

@Inject
private ThreadPool pool;

@AroundInvoke
public Object intercept(InvocationContext ctx) throws Exception {
Future<Object> result = pool.submit(ctx);
return result.get();
}
}
@@ -0,0 +1,28 @@
/*
* 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.thread;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import javax.interceptor.InterceptorBinding;

@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
public @interface Inc {

}
@@ -0,0 +1,33 @@
/*
* 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.thread;

import javax.annotation.Priority;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

@Interceptor
@Inc
@Priority(Interceptor.Priority.APPLICATION + 1)
public class IncInterceptor {

@AroundInvoke
public Object intercept(InvocationContext ctx) throws Exception {
return (Integer) ctx.proceed() + 1;
}
}
@@ -0,0 +1,30 @@
/*
* 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.thread;

@DispatchToThread
@Inc
public class InterceptedBean {

public int foo() {
return bar();
}

public int bar() {
return 1;
}
}

0 comments on commit cd91081

Please sign in to comment.