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

added support for remote call interceptors without the need to annotate ... #80

Merged
merged 1 commit into from Feb 11, 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
Expand Up @@ -17,11 +17,13 @@
package org.jboss.errai.bus.rebind;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.jboss.errai.bus.client.api.base.MessageBuilder;
import org.jboss.errai.bus.client.api.builder.RemoteCallSendable;
import org.jboss.errai.bus.client.framework.AbstractRpcProxy;
import org.jboss.errai.bus.server.annotations.Remote;
import org.jboss.errai.codegen.BlockStatement;
import org.jboss.errai.codegen.DefParameters;
import org.jboss.errai.codegen.Parameter;
Expand All @@ -38,8 +40,13 @@
import org.jboss.errai.codegen.util.ProxyUtil;
import org.jboss.errai.codegen.util.Stmt;
import org.jboss.errai.common.client.api.interceptor.InterceptedCall;
import org.jboss.errai.common.client.api.interceptor.InterceptsRemoteCall;
import org.jboss.errai.common.client.api.interceptor.RemoteCallContext;
import org.jboss.errai.common.client.framework.CallContextStatus;
import org.jboss.errai.common.metadata.RebindUtils;
import org.jboss.errai.config.util.ClassScanner;

import com.google.gwt.core.ext.GeneratorContext;

/**
* Generates an Errai RPC remote proxy.
Expand All @@ -48,9 +55,11 @@
*/
public class RpcProxyGenerator {
private final MetaClass remote;
private final GeneratorContext context;

public RpcProxyGenerator(MetaClass remote) {
public RpcProxyGenerator(MetaClass remote, GeneratorContext context) {
this.remote = remote;
this.context = context;
}

public ClassStructureBuilder<?> generate() {
Expand All @@ -71,8 +80,27 @@ public ClassStructureBuilder<?> generate() {
}

private void generateMethod(ClassStructureBuilder<?> classBuilder, MetaMethod method) {
boolean intercepted =
method.isAnnotationPresent(InterceptedCall.class) || remote.isAnnotationPresent(InterceptedCall.class);
List<Class<?>> interceptors = new ArrayList<Class<?>>();
InterceptedCall interceptedCall = method.getAnnotation(InterceptedCall.class);
if (interceptedCall == null) {
interceptedCall = remote.getAnnotation(InterceptedCall.class);
}
if (interceptedCall == null) {
Collection<MetaClass> interceptorClasses = ClassScanner.getTypesAnnotatedWith(InterceptsRemoteCall.class,
RebindUtils.findTranslatablePackages(context), context);
for (MetaClass interceptorClass : interceptorClasses) {
InterceptsRemoteCall interceptor = interceptorClass.getAnnotation(InterceptsRemoteCall.class);
if (interceptsRemote(interceptor)) {
interceptors.add(interceptorClass.asClass());
}
}
} else {
for (Class<?> class1 : interceptedCall.value()) {
interceptors.add(class1);
}
}

boolean intercepted = interceptors.size() > 0;

Parameter[] parms = DefParameters.from(method).getParameters().toArray(new Parameter[0]);
Parameter[] finalParms = new Parameter[parms.length];
Expand All @@ -91,7 +119,7 @@ private void generateMethod(ClassStructureBuilder<?> classBuilder, MetaMethod me

if (intercepted) {
methodBlock.append(generateInterceptorLogic(classBuilder, method,
generateRequest(classBuilder, method, parameters, true), parmVars));
generateRequest(classBuilder, method, parameters, true), parmVars, interceptors));
}
else {
methodBlock.append(generateRequest(classBuilder, method, parameters, false));
Expand All @@ -105,21 +133,30 @@ private void generateMethod(ClassStructureBuilder<?> classBuilder, MetaMethod me
methodBlock.finish();
}

private Statement generateInterceptorLogic(ClassStructureBuilder<?> classBuilder,
MetaMethod method, Statement requestLogic, List<Statement> parmVars) {

InterceptedCall interceptedCall = method.getAnnotation(InterceptedCall.class);
if (interceptedCall == null) {
interceptedCall = remote.getAnnotation(InterceptedCall.class);
/**
* Returns true if the given {@link InterceptsRemoteCall} is configured to intercept
* the {@link Remote} that we're currently generating proxy code for.
* @param interceptor
*/
private boolean interceptsRemote(InterceptsRemoteCall interceptor) {
Class<?>[] intercepts = interceptor.value();
for (Class<?> iclass : intercepts) {
if (remote.asClass().equals(iclass)) {
return true;
}
}
return false;
}

private Statement generateInterceptorLogic(ClassStructureBuilder<?> classBuilder,
MetaMethod method, Statement requestLogic, List<Statement> parmVars, List<Class<?>> interceptors) {
Statement callContext = ProxyUtil.generateProxyMethodCallContext(RemoteCallContext.class,
classBuilder.getClassDefinition(), method, requestLogic, interceptedCall).finish();
classBuilder.getClassDefinition(), method, requestLogic, interceptors).finish();

return Stmt.try_()
.append(
Stmt.declareVariable(CallContextStatus.class).asFinal().named("status").initializeWith(
Stmt.newObject(CallContextStatus.class).withParameters((Object[]) interceptedCall.value())))
Stmt.newObject(CallContextStatus.class).withParameters(interceptors.toArray())))
.append(
Stmt.declareVariable(RemoteCallContext.class).asFinal().named("callContext")
.initializeWith(callContext))
Expand Down
Expand Up @@ -76,7 +76,7 @@ protected String generate(final TreeLogger logger, final GeneratorContext contex
for (final MetaClass remote : typesAnnotatedWith) {
if (remote.isInterface()) {
// create the remote proxy for this interface
final ClassStructureBuilder<?> remoteProxy = new RpcProxyGenerator(remote).generate();
final ClassStructureBuilder<?> remoteProxy = new RpcProxyGenerator(remote, context).generate();
loadProxies.append(new InnerClass(remoteProxy.getClassDefinition()));

// create the proxy provider
Expand Down
Expand Up @@ -34,6 +34,7 @@
import org.jboss.errai.bus.client.tests.support.SpecificEntity;
import org.jboss.errai.bus.client.tests.support.SubService;
import org.jboss.errai.bus.client.tests.support.TestException;
import org.jboss.errai.bus.client.tests.support.TestInterceptorRPCService;
import org.jboss.errai.bus.client.tests.support.TestRPCService;
import org.jboss.errai.bus.client.tests.support.User;
import org.jboss.errai.common.client.api.RemoteCallback;
Expand Down Expand Up @@ -674,6 +675,38 @@ public void callback(final String response) {
});
}

public void testInterceptedRpc1() {
runAfterInit(new Runnable() {
@Override
public void run() {
MessageBuilder.createCall(new RemoteCallback<String>() {
@Override
public void callback(final String response) {
assertEquals("Request was not intercepted", "intercepted", response);
finishTest();
}
}, TestInterceptorRPCService.class)
.interceptedRpc1();
}
});
}

public void testInterceptedRpc2() {
runAfterInit(new Runnable() {
@Override
public void run() {
MessageBuilder.createCall(new RemoteCallback<String>() {
@Override
public void callback(final String response) {
assertEquals("Request was not intercepted", "intercepted", response);
finishTest();
}
}, TestInterceptorRPCService.class)
.interceptedRpc2();
}
});
}

public void testInterceptedRpcWithChainedInterceptors() {
runAfterInit(new Runnable() {
@Override
Expand Down
Expand Up @@ -17,13 +17,15 @@
package org.jboss.errai.bus.client.tests.support;

import org.jboss.errai.bus.client.api.interceptor.RpcInterceptor;
import org.jboss.errai.common.client.api.interceptor.InterceptsRemoteCall;
import org.jboss.errai.common.client.api.interceptor.RemoteCallContext;

/**
* RPC interceptor for testing purposes. Prevents the actual remote request and sets the result directly.
*
* @author Christian Sadilek <csadilek@redhat.com>
*/
@InterceptsRemoteCall({ TestInterceptorRPCService.class })
public class RpcBypassingInterceptor implements RpcInterceptor {

@Override
Expand Down
@@ -0,0 +1,28 @@
/*
* Copyright 2010 JBoss, a divison Red Hat, Inc
*
* 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.errai.bus.client.tests.support;

import org.jboss.errai.bus.server.annotations.Remote;

@Remote
public interface TestInterceptorRPCService {

public String interceptedRpc1();

public String interceptedRpc2();

}
@@ -0,0 +1,53 @@
/*
* Copyright 2011 JBoss, by Red Hat, Inc
*
* 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.errai.bus.server;

import org.jboss.errai.bus.client.api.base.MessageBuilder;
import org.jboss.errai.bus.client.api.messaging.Message;
import org.jboss.errai.bus.client.api.messaging.MessageCallback;
import org.jboss.errai.bus.client.tests.support.TestInterceptorRPCService;
import org.jboss.errai.bus.server.annotations.Service;

@Service
public class TestInterceptorRPCServiceImpl implements TestInterceptorRPCService, MessageCallback {

/**
* @see org.jboss.errai.bus.client.api.messaging.MessageCallback#callback(org.jboss.errai.bus.client.api.messaging.Message)
*/
@Override
public void callback(Message message) {
MessageBuilder.createConversation(message)
.subjectProvided()
.done().reply();
}

/**
* @see org.jboss.errai.bus.client.tests.support.TestInterceptorRPCService#interceptedRpc1()
*/
@Override
public String interceptedRpc1() {
return "not intercepted";
}

/**
* @see org.jboss.errai.bus.client.tests.support.TestInterceptorRPCService#interceptedRpc2()
*/
@Override
public String interceptedRpc2() {
return "not intercepted";
}
}
Expand Up @@ -18,6 +18,7 @@

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.List;

import org.jboss.errai.codegen.BlockStatement;
import org.jboss.errai.codegen.Parameter;
Expand All @@ -27,13 +28,13 @@
import org.jboss.errai.codegen.builder.AnonymousClassStructureBuilder;
import org.jboss.errai.codegen.builder.BlockBuilder;
import org.jboss.errai.codegen.builder.ElseBlockBuilder;
import org.jboss.errai.codegen.builder.impl.ObjectBuilder;
import org.jboss.errai.codegen.meta.MetaClass;
import org.jboss.errai.codegen.meta.MetaClassFactory;
import org.jboss.errai.codegen.meta.MetaMethod;
import org.jboss.errai.codegen.meta.MetaParameter;
import org.jboss.errai.common.client.api.ErrorCallback;
import org.jboss.errai.common.client.api.RemoteCallback;
import org.jboss.errai.common.client.api.interceptor.InterceptedCall;
import org.jboss.errai.common.client.api.interceptor.RemoteCallContext;

import com.google.common.reflect.TypeToken;
Expand All @@ -59,15 +60,14 @@ private ProxyUtil() {}
* @param proceed
* the logic that should be invoked if
* {@link org.jboss.errai.common.client.api.interceptor.CallContext#proceed()} is called.
* @param interceptedCall
* a reference to the {@link org.jboss.errai.common.client.api.interceptor.InterceptedCall} annotation on the
* remote interface or method
* @param interceptors
* a list of interceptors to use
* @return statement representing an anonymous implementation of the provided
* {@link org.jboss.errai.common.client.api.interceptor.CallContext}
*/
public static AnonymousClassStructureBuilder generateProxyMethodCallContext(
final Class<? extends RemoteCallContext> callContextType,
final MetaClass proxyClass, final MetaMethod method, final Statement proceed, final InterceptedCall interceptedCall) {
final MetaClass proxyClass, final MetaMethod method, final Statement proceed, final List<Class<?>> interceptors) {

return Stmt.newObject(callContextType).extend()
.publicOverridesMethod("getMethodName")
Expand All @@ -77,7 +77,7 @@ public static AnonymousClassStructureBuilder generateProxyMethodCallContext(
.append(Stmt.load(method.getAnnotations()).returnValue())
.finish()
.publicOverridesMethod("proceed")
.append(generateInterceptorStackProceedMethod(proceed, interceptedCall))
.append(generateInterceptorStackProceedMethod(proceed, interceptors))
.append(Stmt.load(null).returnValue())
.finish()
.publicOverridesMethod("proceed", Parameter.of(RemoteCallback.class, "interceptorCallback", true))
Expand Down Expand Up @@ -122,18 +122,19 @@ public static AnonymousClassStructureBuilder generateProxyMethodCallContext(
.finish();
}

private static Statement generateInterceptorStackProceedMethod(final Statement proceed, final InterceptedCall interceptedCall) {
private static Statement generateInterceptorStackProceedMethod(final Statement proceed, final List<Class<?>> interceptors) {
final BlockStatement proceedLogic = new BlockStatement();
proceedLogic.addStatement(Stmt.loadVariable("status").invoke("proceed"));

final BlockBuilder<ElseBlockBuilder> interceptorStack =
If.isNotNull(Stmt.loadVariable("status").invoke("getNextInterceptor"));

for (final Class<?> interceptor : interceptedCall.value()) {
for (final Class<?> interceptor : interceptors) {
interceptorStack.append(If.cond(Bool.equals(
Stmt.loadVariable("status").invoke("getNextInterceptor"), interceptor))
.append(Stmt.loadVariable("status").invoke("setProceeding", false))
.append(
// TODO if IOC, try to get a managed instance. If not IOC or if no managed instance is available, new up the interceptor
Stmt.nestedCall(Stmt.newObject(interceptor))
.invoke("aroundInvoke", Variable.get("this")))
.append(
Expand Down
@@ -0,0 +1,45 @@
/*
* Copyright 2011 JBoss, by Red Hat, Inc
*
* 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.errai.common.client.api.interceptor;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* This annotation is used to indicate that a particular class should be
* used as an interceptor for a remote interface (e.g. RPC or REST
* interface). This works similarly to the {@link InterceptedCall}
* annotation but has the added benefit that it does not require the
* remote interface to be annotated.
*
* The class that is annotated should implement the {@link RemoteCallInterceptor}
* interface.
*/
@Documented
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface InterceptsRemoteCall {

/**
* The interceptor type(s) to use. Interceptor execution is guaranteed to be
* in declaration order.
*/
Class<?>[] value();
}