com.google.web.bindery.requestfactory.vm.InProcessRequestContext Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vaadin-client Show documentation
Show all versions of vaadin-client Show documentation
Vaadin is a web application framework for Rich Internet Applications (RIA).
Vaadin enables easy development and maintenance of fast and
secure rich web
applications with a stunning look and feel and a wide browser support.
It features a server-side architecture with the majority of the logic
running
on the server. Ajax technology is used at the browser-side to ensure a
rich
and interactive user experience.
/*
* Copyright 2010 Google 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 com.google.web.bindery.requestfactory.vm;
import com.google.web.bindery.autobean.shared.AutoBean;
import com.google.web.bindery.autobean.shared.AutoBean.PropertyName;
import com.google.web.bindery.autobean.shared.AutoBeanFactory;
import com.google.web.bindery.autobean.vm.impl.BeanMethod;
import com.google.web.bindery.autobean.vm.impl.TypeUtils;
import com.google.web.bindery.requestfactory.shared.BaseProxy;
import com.google.web.bindery.requestfactory.shared.InstanceRequest;
import com.google.web.bindery.requestfactory.shared.JsonRpcContent;
import com.google.web.bindery.requestfactory.shared.JsonRpcWireName;
import com.google.web.bindery.requestfactory.shared.Request;
import com.google.web.bindery.requestfactory.shared.RequestContext;
import com.google.web.bindery.requestfactory.shared.impl.AbstractRequest;
import com.google.web.bindery.requestfactory.shared.impl.AbstractRequestContext;
import com.google.web.bindery.requestfactory.shared.impl.RequestData;
import com.google.web.bindery.requestfactory.shared.impl.SimpleProxyId;
import com.google.web.bindery.requestfactory.vm.impl.Deobfuscator;
import com.google.web.bindery.requestfactory.vm.impl.OperationKey;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Collection;
/**
* An in-process implementation of RequestContext.
*/
class InProcessRequestContext extends AbstractRequestContext {
class RequestContextHandler implements InvocationHandler {
public InProcessRequestContext getContext() {
return InProcessRequestContext.this;
}
public Object invoke(Object proxy, Method method, final Object[] args) throws Throwable {
// Maybe delegate to superclass
Class> owner = method.getDeclaringClass();
if (Object.class.equals(owner) || RequestContext.class.equals(owner)
|| AbstractRequestContext.class.equals(owner)) {
try {
return method.invoke(InProcessRequestContext.this, args);
} catch (InvocationTargetException e) {
throw e.getCause();
}
}
/*
* Instance methods treat the 0-th argument as the instance on which to
* invoke the method.
*/
final Object[] actualArgs;
Type returnGenericType;
boolean isInstance = InstanceRequest.class.isAssignableFrom(method.getReturnType());
if (isInstance) {
returnGenericType =
TypeUtils.getParameterization(InstanceRequest.class, method.getGenericReturnType(),
method.getReturnType())[1];
if (args == null) {
actualArgs = new Object[1];
} else {
// Save a slot for the this argument
actualArgs = new Object[args.length + 1];
System.arraycopy(args, 0, actualArgs, 1, args.length);
}
} else {
returnGenericType =
TypeUtils.getSingleParameterization(Request.class, method.getGenericReturnType(),
method.getReturnType());
if (args == null) {
actualArgs = NO_ARGS;
} else {
actualArgs = args;
}
}
Class> returnType = TypeUtils.ensureBaseType(returnGenericType);
Class> elementType =
Collection.class.isAssignableFrom(returnType) ? TypeUtils.ensureBaseType(TypeUtils
.getSingleParameterization(Collection.class, returnGenericType)) : null;
final RequestData data;
if (dialect.equals(Dialect.STANDARD)) {
StringBuilder descriptor = new StringBuilder("(");
for (Class> param : method.getParameterTypes()) {
appendDescriptor(descriptor, param);
}
// Don't care about the return type
descriptor.append(")V");
OperationKey operation =
new OperationKey(context.getName(), method.getName(), descriptor.toString());
data = new RequestData(operation.get(), actualArgs, returnType, elementType);
} else {
// Calculate request metadata
JsonRpcWireName wireInfo = method.getReturnType().getAnnotation(JsonRpcWireName.class);
String apiVersion = wireInfo.version();
String operation = wireInfo.value();
int foundContent = -1;
final String[] parameterNames = args == null ? new String[0] : new String[args.length];
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
parameter : for (int i = 0, j = parameterAnnotations.length; i < j; i++) {
for (Annotation annotation : parameterAnnotations[i]) {
if (PropertyName.class.equals(annotation.annotationType())) {
parameterNames[i] = ((PropertyName) annotation).value();
continue parameter;
} else if (JsonRpcContent.class.equals(annotation.annotationType())) {
foundContent = i;
continue parameter;
}
}
throw new UnsupportedOperationException("No " + PropertyName.class.getCanonicalName()
+ " annotation on parameter " + i + " of method " + method.toString());
}
final int contentIdx = foundContent;
data = new RequestData(operation, actualArgs, returnType, elementType);
for (int i = 0, j = parameterNames.length; i < j; i++) {
if (i != contentIdx) {
data.setNamedParameter(parameterNames[i], args[i]);
} else {
data.setRequestContent(args[i]);
}
}
data.setApiVersion(apiVersion);
}
// Create the request, just filling in the RequestData details
final AbstractRequest req =
new AbstractRequest(InProcessRequestContext.this) {
@Override
protected RequestData makeRequestData() {
data.setPropertyRefs(propertyRefs);
return data;
}
};
if (!isInstance) {
// Instance invocations are enqueued when using() is called
addInvocation(req);
}
if (dialect.equals(Dialect.STANDARD)) {
return req;
} else if (dialect.equals(Dialect.JSON_RPC)) {
// Support optional parameters for JSON-RPC payloads
Class> requestType = method.getReturnType().asSubclass(Request.class);
return Proxy.newProxyInstance(requestType.getClassLoader(), new Class>[] {requestType},
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())
|| Request.class.equals(method.getDeclaringClass())) {
return method.invoke(req, args);
} else if (BeanMethod.SET.matches(method) || BeanMethod.SET_BUILDER.matches(method)) {
req.getRequestData().setNamedParameter(BeanMethod.SET.inferName(method), args[0]);
return Void.TYPE.equals(method.getReturnType()) ? null : proxy;
}
throw new UnsupportedOperationException(method.toString());
}
});
} else {
throw new RuntimeException("Should not reach here");
}
}
private void appendDescriptor(StringBuilder descriptor, Class> param) {
// Arrays aren't actually used anywhere in RequestFactory, but it's trivial to
// implement and might be useful later on.
while (param.isArray()) {
descriptor.append('[');
param = param.getComponentType();
}
if (param.isPrimitive()) {
if (param == Boolean.TYPE) {
descriptor.append('Z');
} else if (param == Byte.TYPE) {
descriptor.append('B');
} else if (param == Character.TYPE) {
descriptor.append('C');
} else if (param == Double.TYPE) {
descriptor.append('D');
} else if (param == Float.TYPE) {
descriptor.append('F');
} else if (param == Integer.TYPE) {
descriptor.append('I');
} else if (param == Long.TYPE) {
descriptor.append('J');
} else if (param == Short.TYPE) {
descriptor.append('S');
} else {
assert param == Void.TYPE;
descriptor.append('V');
}
} else {
descriptor.append('L').append(param.getName().replace('.', '/')).append(';');
}
}
}
static final Object[] NO_ARGS = new Object[0];
private final Class extends RequestContext> context;
private final Deobfuscator deobfuscator;
private final Dialect dialect;
protected InProcessRequestContext(InProcessRequestFactory factory, Dialect dialect,
Class extends RequestContext> context) {
super(factory, dialect);
this.context = context;
this.deobfuscator = factory.getDeobfuscator();
this.dialect = dialect;
}
@Override
public T append(T other) {
RequestContextHandler h = (RequestContextHandler) Proxy.getInvocationHandler(other);
super.append(h.getContext());
return other;
}
@Override
protected AutoBean createProxy(Class clazz, SimpleProxyId id,
boolean useAppendedContexts) {
if (deobfuscator.isReferencedType(clazz.getName())) {
return super.createProxy(clazz, id, useAppendedContexts);
}
throw new IllegalArgumentException("Unknown proxy type " + clazz.getName());
}
@Override
protected AutoBeanFactory getAutoBeanFactory() {
return ((InProcessRequestFactory) getRequestFactory()).getAutoBeanFactory();
}
}