Bye Bye Boilerplate patch. No static configuration required for servlet
beyond @Service annotations on request interfaces and
@DataTransferObject annotations on Record types.
TOKEN no longer required on Record types, as the class itself is used as
a token.
Introduced OperationRegistry and RequestSecurityProvider interfaces.
Removed the no longer used annotations: ServerType, ServerOperation. Deleted
ExpensesServerSideOperations.
Patch is updated version of http://gwt-code-reviews.appspot.com/661801
Patch by: cromwellian, amitmanjhi
Review by: amitmanjhi
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8309 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/bikeshed/src/com/google/gwt/app/place/AbstractRecordEditActivity.java b/bikeshed/src/com/google/gwt/app/place/AbstractRecordEditActivity.java
index 6da56eb..627b1b4 100644
--- a/bikeshed/src/com/google/gwt/app/place/AbstractRecordEditActivity.java
+++ b/bikeshed/src/com/google/gwt/app/place/AbstractRecordEditActivity.java
@@ -132,7 +132,7 @@
if (creating) {
// TODO shouldn't have to cast like this. Let's get something better than
// a string token
- R tempRecord = (R) deltas.create(getRecordToken());
+ R tempRecord = (R) deltas.create(getRecordClass());
futureId = tempRecord.getId();
doStart(display, tempRecord);
} else {
@@ -169,7 +169,7 @@
* Called to fetch the string token needed to get a new record via
* {@link DeltaValueStore#create}.
*/
- protected abstract String getRecordToken();
+ protected abstract Class getRecordClass();
private void doStart(final Display display, R record) {
view.setEnabled(true);
diff --git a/bikeshed/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java b/bikeshed/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java
index 309ffd5..4dfab30 100644
--- a/bikeshed/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java
+++ b/bikeshed/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java
@@ -35,9 +35,9 @@
import com.google.gwt.requestfactory.client.impl.AbstractLongRequest;
import com.google.gwt.requestfactory.client.impl.ClientRequestHelper;
import com.google.gwt.requestfactory.client.impl.RequestFactoryJsonImpl;
+import com.google.gwt.requestfactory.server.ReflectionBasedOperationRegistry;
import com.google.gwt.requestfactory.shared.RecordListRequest;
import com.google.gwt.requestfactory.shared.RecordRequest;
-import com.google.gwt.requestfactory.shared.ServerOperation;
import com.google.gwt.requestfactory.shared.impl.RequestDataManager;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
@@ -322,16 +322,11 @@
SourceWriter sw = f.createSourceWriter(generatorContext, out);
sw.println();
- sw.println("public RecordSchema<? extends Record> getType(String token) {");
+ sw.println("public RecordSchema<? extends Record> getType(Class token) {");
sw.indent();
for (JClassType publicRecordType : generatedRecordTypes) {
- if (publicRecordType.getField("TOKEN") == null) {
- logger.log(TreeLogger.ERROR, "Record type "
- + publicRecordType.getQualifiedSourceName()
- + " should have a field TOKEN");
- throw new UnableToCompleteException();
- }
- sw.println("if (token == " + publicRecordType.getName() + ".TOKEN) {");
+
+ sw.println("if (token == " + publicRecordType.getName() + ".class) {");
sw.indent();
sw.println("return " + publicRecordType.getName() + "Impl.SCHEMA;");
sw.outdent();
@@ -385,12 +380,8 @@
ensureRecordType(logger, generatorContext,
returnType.getPackage().getName(), returnType);
- ServerOperation annotation = method.getAnnotation(ServerOperation.class);
- if (annotation == null) {
- logger.log(TreeLogger.ERROR, "no annotation on the service method "
- + method);
- throw new UnableToCompleteException();
- }
+ String operationName = interfaceType.getQualifiedSourceName()
+ + ReflectionBasedOperationRegistry.SCOPE_SEPARATOR + method.getName();
JClassType requestType = method.getReturnType().isClassOrInterface();
String requestClassName = null;
@@ -420,7 +411,7 @@
sw.indent();
sw.println("return " + ClientRequestHelper.class.getSimpleName()
+ ".getRequestString(" + RequestDataManager.class.getSimpleName()
- + ".getRequestMap(\"" + annotation.value() + "\", "
+ + ".getRequestMap(\"" + operationName + "\", "
+ getParametersAsString(method) + ", null));");
sw.outdent();
sw.println("}");
@@ -617,12 +608,10 @@
sw.println("}");
sw.println();
- sw.println("public String getToken() {");
+ sw.println("public Class getToken() {");
sw.indent();
- String fieldName = "TOKEN";
- validateTokenField(publicRecordType, fieldName, typeOracle, logger);
- sw.println("return " + publicRecordType.getName() + "." + fieldName
- + "; // special field");
+ sw.println("return " + publicRecordType.getQualifiedSourceName() + ".class;"
+ + " // special field");
sw.outdent();
sw.println("}");
diff --git a/bikeshed/src/com/google/gwt/requestfactory/server/DefaultSecurityProvider.java b/bikeshed/src/com/google/gwt/requestfactory/server/DefaultSecurityProvider.java
new file mode 100644
index 0000000..4b92239
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/requestfactory/server/DefaultSecurityProvider.java
@@ -0,0 +1,44 @@
+/*
+ * 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.gwt.requestfactory.server;
+
+import com.google.gwt.requestfactory.shared.Service;
+
+/**
+ * A security provider that enforces {@link com.google.gwt.requestfactory.shared.Service}
+ * annotations.
+ */
+public class DefaultSecurityProvider implements RequestSecurityProvider {
+
+ public void checkClass(Class<?> clazz) throws SecurityException {
+ Service service = clazz.getAnnotation(Service.class);
+ if (service == null) {
+ throw new SecurityException(
+ "Class " + clazz.getName() + " does not have a @Service annotation.");
+ }
+ try {
+ Class.forName(service.value().getCanonicalName());
+ } catch (ClassNotFoundException e) {
+ throw new SecurityException(
+ "Class " + service.value() + " from @Service annotation on " + clazz
+ + " cannot be loaded.");
+ }
+ }
+
+ public String mapOperation(String operationName) throws SecurityException {
+ return operationName;
+ }
+}
diff --git a/bikeshed/src/com/google/gwt/requestfactory/shared/ServerOperation.java b/bikeshed/src/com/google/gwt/requestfactory/server/OperationRegistry.java
similarity index 60%
rename from bikeshed/src/com/google/gwt/requestfactory/shared/ServerOperation.java
rename to bikeshed/src/com/google/gwt/requestfactory/server/OperationRegistry.java
index 5ea31a3..ef8a538 100644
--- a/bikeshed/src/com/google/gwt/requestfactory/shared/ServerOperation.java
+++ b/bikeshed/src/com/google/gwt/requestfactory/server/OperationRegistry.java
@@ -13,19 +13,14 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
-package com.google.gwt.requestfactory.shared;
+package com.google.gwt.requestfactory.server;
+
+import com.google.gwt.requestfactory.shared.RequestFactory;
/**
- * <p>
- * <span style="color:red">Experimental API: This class is still under rapid
- * development, and is very likely to be deleted. Use it at your own risk.
- * </span>
- * </p>
- * Annotation to mark RPC methods that specifies details about the operation to
- * be invoked on the server.
+ * Maps operation name to {RequestDefinition}.
*/
-public @interface ServerOperation {
+public interface OperationRegistry {
- String value();
-
+ RequestFactory.RequestDefinition getOperation(String operationName);
}
diff --git a/bikeshed/src/com/google/gwt/requestfactory/server/ReflectionBasedOperationRegistry.java b/bikeshed/src/com/google/gwt/requestfactory/server/ReflectionBasedOperationRegistry.java
new file mode 100644
index 0000000..529e7c0
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/requestfactory/server/ReflectionBasedOperationRegistry.java
@@ -0,0 +1,190 @@
+/*
+ * 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.gwt.requestfactory.server;
+
+import com.google.gwt.requestfactory.shared.DataTransferObject;
+import com.google.gwt.requestfactory.shared.RequestFactory;
+import com.google.gwt.requestfactory.shared.Service;
+import com.google.gwt.valuestore.shared.Record;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.List;
+
+/**
+ * OperationRegistry which uses the operation name as a convention for
+ * reflection to a method on a class, and returns an appropriate {@link
+ * com.google.gwt.requestfactory.shared.RequestFactory.RequestDefinition}.
+ */
+public class ReflectionBasedOperationRegistry implements OperationRegistry {
+
+ class ReflectiveRequestDefinition
+ implements RequestFactory.RequestDefinition {
+
+ private Class<?> requestClass;
+
+ private Method requestMethod;
+
+ private Class<?> domainClass;
+
+ private Method domainMethod;
+
+ public ReflectiveRequestDefinition(Class<?> requestClass,
+ Method requestMethod, Class<?> domainClass, Method domainMethod) {
+ this.requestClass = requestClass;
+ this.requestMethod = requestMethod;
+ this.domainClass = domainClass;
+ this.domainMethod = domainMethod;
+ }
+
+ public String getDomainClassName() {
+ return domainClass.getCanonicalName();
+ }
+
+ public String getDomainMethodName() {
+ return domainMethod.getName();
+ }
+
+ public Class<?>[] getParameterTypes() {
+ return domainMethod.getParameterTypes();
+ }
+
+ public Class<?> getReturnType() {
+ Class<?> domainReturnType = getReturnTypeFromParameter(domainMethod,
+ domainMethod.getGenericReturnType());
+ Class<?> requestReturnType = getReturnTypeFromParameter(requestMethod,
+ requestMethod.getGenericReturnType());
+ if (Record.class.isAssignableFrom(requestReturnType)) {
+ DataTransferObject annotation =
+ requestReturnType.getAnnotation(DataTransferObject.class);
+ if (annotation != null) {
+ Class<?> dtoClass = annotation.value();
+ if (!dtoClass.equals(domainReturnType)) {
+ throw new IllegalArgumentException(
+ "Type mismatch between " + domainMethod + " return type, and "
+ + requestReturnType + "'s DataTransferObject annotation "
+ + dtoClass);
+ }
+ } else {
+ throw new IllegalArgumentException(
+ "Missing DataTransferObject " + "annotation on record type "
+ + requestReturnType);
+ }
+ return requestReturnType;
+ }
+ // primitive ?
+ return requestReturnType;
+ }
+
+ public boolean isReturnTypeList() {
+ return List.class.isAssignableFrom(domainMethod.getReturnType());
+ }
+
+ public String name() {
+ return requestClass.getCanonicalName() + SCOPE_SEPARATOR
+ + getDomainMethodName();
+ }
+
+ private Class<?> getReturnTypeFromParameter(Method method, Type type) {
+ if (type instanceof ParameterizedType) {
+ ParameterizedType pType = (ParameterizedType) type;
+ Class<?> rawType = (Class<?>) pType.getRawType();
+ if (List.class.isAssignableFrom(rawType)
+ || RequestFactory.RequestObject.class.isAssignableFrom(rawType)) {
+ Class<?> rType = getTypeArgument(pType);
+ if (rType != null) {
+ if (List.class.isAssignableFrom(rType)) {
+ return getReturnTypeFromParameter(method, rType);
+ }
+ return rType;
+ }
+ throw new IllegalArgumentException(
+ "Bad or missing type arguments for "
+ + "List return type on method " + method);
+ }
+
+ } else {
+ // Primitive?
+ return (Class<?>) type;
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Class<?> getTypeArgument(ParameterizedType type) {
+ Type[] params = type.getActualTypeArguments();
+ if (params.length == 1) {
+ return (Class<Object>) params[0];
+ }
+
+ return null;
+ }
+ }
+
+ public static final String SCOPE_SEPARATOR = "::";
+
+ private RequestSecurityProvider securityProvider;
+
+ public ReflectionBasedOperationRegistry(
+ RequestSecurityProvider securityProvider) {
+ this.securityProvider = securityProvider;
+ }
+
+ /**
+ * Turns an operation in the form of package.requestClass::method into a
+ * RequestDefinition via reflection.
+ */
+ public RequestFactory.RequestDefinition getOperation(String operationName) {
+ String decodedOperationName = securityProvider.mapOperation(operationName);
+ String parts[] = decodedOperationName.split(SCOPE_SEPARATOR);
+ final String reqClassName = parts[0];
+ final String domainMethodName = parts[1];
+ try {
+ // Do not invoke static initializer before checking if this class can be
+ // legally invoked
+ Class<?> requestClass = Class.forName(reqClassName, false,
+ this.getClass().getClassLoader());
+ securityProvider.checkClass(requestClass);
+ Service domainClassAnnotation = requestClass.getAnnotation(Service.class);
+ if (domainClassAnnotation != null) {
+ Class<?> domainClass = domainClassAnnotation.value();
+ Method requestMethod = findMethod(requestClass, domainMethodName);
+ Method domainMethod = findMethod(domainClass, domainMethodName);
+ if (requestMethod != null && domainMethod != null) {
+ return new ReflectiveRequestDefinition(requestClass, requestMethod,
+ domainClass, domainMethod);
+ }
+ }
+ return null;
+ } catch (ClassNotFoundException e) {
+ throw new SecurityException(
+ "Access to non-existent class " + reqClassName);
+ }
+ }
+
+ private Method findMethod(Class<?> clazz, String methodName) {
+ for (Method method : clazz.getDeclaredMethods()) {
+ if ((method.getModifiers() & Modifier.PUBLIC) != 0) {
+ if (method.getName().equals(methodName)) {
+ return method;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/bikeshed/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java b/bikeshed/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
index 43f20a8..50a59d5 100644
--- a/bikeshed/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
+++ b/bikeshed/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
@@ -15,9 +15,8 @@
*/
package com.google.gwt.requestfactory.server;
+import com.google.gwt.requestfactory.shared.DataTransferObject;
import com.google.gwt.requestfactory.shared.RequestFactory;
-import com.google.gwt.requestfactory.shared.ServerType;
-import com.google.gwt.requestfactory.shared.RequestFactory.Config;
import com.google.gwt.requestfactory.shared.RequestFactory.RequestDefinition;
import com.google.gwt.requestfactory.shared.impl.RequestDataManager;
import com.google.gwt.valuestore.shared.Property;
@@ -92,8 +91,6 @@
private static final Set<String> BLACK_LIST = initBlackList();
- private static final String SERVER_OPERATION_CONTEXT_PARAM = "servlet.serverOperation";
-
private static Set<String> initBlackList() {
Set<String> blackList = new HashSet<String>();
for (String str : new String[] {"password"}) {
@@ -102,9 +99,7 @@
return Collections.unmodifiableSet(blackList);
}
- private Config config = null;
-
- protected Map<String, EntityRecordPair> tokenToEntityRecord;
+ private OperationRegistry operationRegistry;
@SuppressWarnings("unchecked")
@Override
@@ -208,8 +203,9 @@
JSONObject recordObject, WriteOperation writeOperation) {
try {
- Class<?> entity = tokenToEntityRecord.get(recordToken).entity;
- Class<? extends Record> record = tokenToEntityRecord.get(recordToken).record;
+ Class<? extends Record> record = getRecordFromClassToken(recordToken);
+ Class<?> entity = getEntityFromRecordAnnotation(record);
+
Map<String, Class<?>> propertiesInRecord = getPropertiesFromRecord(record);
validateKeys(recordObject, propertiesInRecord.keySet());
updatePropertyTypes(propertiesInRecord, entity);
@@ -277,70 +273,8 @@
@SuppressWarnings("unchecked")
private void ensureConfig() {
- if (config == null) {
- synchronized (this) {
- if (config != null) {
- return;
- }
- try {
- final String serverOperation = getServletContext().getInitParameter(
- SERVER_OPERATION_CONTEXT_PARAM);
- if (null == serverOperation) {
- failConfig();
- }
- Class<?> clazz = Class.forName(serverOperation);
- if (Config.class.isAssignableFrom(clazz)) {
- config = ((Class<? extends Config>) clazz).newInstance();
-
- // initialize tokenToEntity map
- tokenToEntityRecord = new HashMap<String, EntityRecordPair>();
- for (Class<? extends Record> recordClass : config.recordTypes()) {
- ServerType serverType = recordClass.getAnnotation(ServerType.class);
- String token = (String) recordClass.getField("TOKEN").get(null);
- if (token == null) {
- throw new IllegalStateException("TOKEN field on "
- + recordClass.getName() + " can not be null");
- }
- EntityRecordPair previousValue = tokenToEntityRecord.get(token);
- if (previousValue != null) {
- throw new IllegalStateException(
- "TOKEN fields have to be unique. TOKEN fields for both "
- + recordClass.getName() + " and "
- + previousValue.record.getName()
- + " have the same value, value = " + token);
- }
- tokenToEntityRecord.put(token, new EntityRecordPair(
- serverType.type(), recordClass));
- }
- }
-
- } catch (ClassNotFoundException e) {
- failConfig(e);
- } catch (InstantiationException e) {
- failConfig(e);
- } catch (IllegalAccessException e) {
- failConfig(e);
- } catch (SecurityException e) {
- failConfig(e);
- } catch (ClassCastException e) {
- failConfig(e);
- } catch (NoSuchFieldException e) {
- failConfig(e);
- }
- }
- }
- }
-
- private void failConfig() {
- failConfig(null);
- }
-
- private void failConfig(Throwable e) {
- final String message = String.format("Context parameter \"%s\" must name "
- + "a default instantiable configuration class implementing %s",
- SERVER_OPERATION_CONTEXT_PARAM, RequestFactory.Config.class.getName());
-
- throw new IllegalStateException(message, e);
+ operationRegistry = new ReflectionBasedOperationRegistry(
+ new DefaultSecurityProvider());
}
private String getContent(HttpServletRequest request) throws IOException {
@@ -361,6 +295,16 @@
}
}
+ private Class<Object> getEntityFromRecordAnnotation(
+ Class<? extends Record> record) {
+ DataTransferObject dtoAnn = record.getAnnotation(DataTransferObject.class);
+ if (dtoAnn != null) {
+ return (Class<Object>) dtoAnn.value();
+ }
+ throw new IllegalArgumentException("Record class " + record.getName()
+ + " missing DataTransferObject annotation");
+ }
+
private Object getEntityInstance(WriteOperation writeOperation,
Class<?> entity, Object idValue, Class<?> idType)
throws SecurityException, InstantiationException, IllegalAccessException,
@@ -432,7 +376,7 @@
private RequestDefinition getOperation(String operationName) {
RequestDefinition operation;
- operation = config.requestDefinitions().get(operationName);
+ operation = operationRegistry.getOperation(operationName);
if (null == operation) {
throw new IllegalArgumentException("Unknown operation " + operationName);
}
@@ -518,6 +462,21 @@
return recordObject.get(key);
}
+ private Class<Record> getRecordFromClassToken(String recordToken) {
+ try {
+ Class<?> clazz = Class.forName(recordToken, false,
+ getClass().getClassLoader());
+ if (Record.class.isAssignableFrom(clazz)) {
+ return (Class<Record>) clazz;
+ }
+ throw new SecurityException(
+ "Attempt to access non-record class " + recordToken);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException(
+ "Non-existent record class " + recordToken);
+ }
+ }
+
private JSONObject getReturnRecord(WriteOperation writeOperation,
Object entityInstance, JSONObject recordObject,
Set<ConstraintViolation<Object>> violations) throws SecurityException,
diff --git a/bikeshed/src/com/google/gwt/requestfactory/server/RequestSecurityProvider.java b/bikeshed/src/com/google/gwt/requestfactory/server/RequestSecurityProvider.java
new file mode 100644
index 0000000..e95ad31
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/requestfactory/server/RequestSecurityProvider.java
@@ -0,0 +1,34 @@
+/*
+ * 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.gwt.requestfactory.server;
+
+/**
+ * Enforces security policy for operations and classes, as well as permitting
+ * request obfuscation.
+ */
+public interface RequestSecurityProvider {
+
+ /**
+ * Throws exception if argument is not accessible via remote requests.
+ */
+ void checkClass(Class<?> clazz) throws SecurityException;
+
+ /**
+ * Optionally decodes a previously encoded operation. Throws exception if
+ * argument is not a legal operation.
+ */
+ String mapOperation(String operationName) throws SecurityException;
+}
diff --git a/bikeshed/src/com/google/gwt/requestfactory/shared/ServerType.java b/bikeshed/src/com/google/gwt/requestfactory/shared/DataTransferObject.java
similarity index 77%
rename from bikeshed/src/com/google/gwt/requestfactory/shared/ServerType.java
rename to bikeshed/src/com/google/gwt/requestfactory/shared/DataTransferObject.java
index f0df3c7..21c505e 100644
--- a/bikeshed/src/com/google/gwt/requestfactory/shared/ServerType.java
+++ b/bikeshed/src/com/google/gwt/requestfactory/shared/DataTransferObject.java
@@ -21,17 +21,14 @@
import java.lang.annotation.Target;
/**
- * <p>
- * <span style="color:red">Experimental API: This class is still under rapid
+ * <p> <span style="color:red">Experimental API: This class is still under rapid
* development, and is very likely to be deleted. Use it at your own risk.
- * </span>
- * </p>
- * Annotation on Record classes specifying 'type'. 'type' represents the
- * server-side counterpart of the Record.
+ * </span> </p> Annotation on Record classes specifying 'type'. 'type'
+ * represents the server-side counterpart of the Record.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
-public @interface ServerType {
+public @interface DataTransferObject {
- Class<?> type();
+ Class<?> value();
}
diff --git a/bikeshed/src/com/google/gwt/requestfactory/shared/RequestFactory.java b/bikeshed/src/com/google/gwt/requestfactory/shared/RequestFactory.java
index 8b81041..518fe47 100644
--- a/bikeshed/src/com/google/gwt/requestfactory/shared/RequestFactory.java
+++ b/bikeshed/src/com/google/gwt/requestfactory/shared/RequestFactory.java
@@ -17,12 +17,8 @@
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.valuestore.shared.DeltaValueStore;
-import com.google.gwt.valuestore.shared.Record;
import com.google.gwt.valuestore.shared.ValueStore;
-import java.util.Map;
-import java.util.Set;
-
/**
* <p>
* <span style="color:red">Experimental API: This class is still under rapid
@@ -34,18 +30,6 @@
public interface RequestFactory {
// TODO all these inner interfaces are clutter, move them to their own files
-
- /**
- * Implemented by the configuration class used by
- * {@link com.google.gwt.requestfactory.server.RequestFactoryServlet
- * RequestFactoryServlet}.
- */
- interface Config {
- Map<String, RequestDefinition> requestDefinitions();
-
- Set<Class<? extends Record>> recordTypes();
- }
-
/**
* Implemented by enums that define the mapping between request objects and
* service methods.
diff --git a/bikeshed/src/com/google/gwt/requestfactory/shared/ServerType.java b/bikeshed/src/com/google/gwt/requestfactory/shared/Service.java
similarity index 71%
copy from bikeshed/src/com/google/gwt/requestfactory/shared/ServerType.java
copy to bikeshed/src/com/google/gwt/requestfactory/shared/Service.java
index f0df3c7..59f2cf5 100644
--- a/bikeshed/src/com/google/gwt/requestfactory/shared/ServerType.java
+++ b/bikeshed/src/com/google/gwt/requestfactory/shared/Service.java
@@ -21,17 +21,12 @@
import java.lang.annotation.Target;
/**
- * <p>
- * <span style="color:red">Experimental API: This class is still under rapid
- * development, and is very likely to be deleted. Use it at your own risk.
- * </span>
- * </p>
- * Annotation on Record classes specifying 'type'. 'type' represents the
- * server-side counterpart of the Record.
+ * Annotation on Request classes specifying the server side implementations that
+ * back them.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
-public @interface ServerType {
+public @interface Service {
- Class<?> type();
+ Class<?> value();
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileExpenseEntry.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileExpenseEntry.java
index 8a8734b..a7b1a4a 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileExpenseEntry.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileExpenseEntry.java
@@ -75,7 +75,7 @@
public void create(String reportId) {
deltas = requestFactory.getValueStore().spawnDeltaView();
- expense = (ExpenseRecord) deltas.create(ExpenseRecord.TOKEN);
+ expense = (ExpenseRecord) deltas.create(ExpenseRecord.class);
deltas.set(ExpenseRecord.reportId, expense, reportId);
displayExpense();
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileReportEntry.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileReportEntry.java
index 0207b78..7e48a4f 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileReportEntry.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/MobileReportEntry.java
@@ -85,7 +85,7 @@
public void create(Long reporterId) {
deltas = requestFactory.getValueStore().spawnDeltaView();
- report = (ReportRecord) deltas.create(ReportRecord.TOKEN);
+ report = (ReportRecord) deltas.create(ReportRecord.class);
deltas.set(ReportRecord.reporterKey, report, reporterId.toString());
displayReport();
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/EmployeeRecord.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/EmployeeRecord.java
index 55cd630..54e0c17 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/EmployeeRecord.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/EmployeeRecord.java
@@ -15,7 +15,7 @@
*/
package com.google.gwt.sample.expenses.gwt.request;
-import com.google.gwt.requestfactory.shared.ServerType;
+import com.google.gwt.requestfactory.shared.DataTransferObject;
import com.google.gwt.valuestore.shared.Property;
import com.google.gwt.valuestore.shared.Record;
@@ -25,15 +25,9 @@
* <p>
* IRL this class will be generated by a JPA-savvy tool run before compilation.
*/
-@ServerType(type = com.google.gwt.sample.expenses.server.domain.Employee.class)
+@DataTransferObject(com.google.gwt.sample.expenses.server.domain.Employee.class)
public interface EmployeeRecord extends Record {
- /**
- * Used as input to {@link com.google.gwt.valuestore.shared.DeltaValueStore#create(Record)
- * DeltaValueStore#create()} and in the wire format during sync requests.
- */
- String TOKEN = "EmployeeRecord";
-
Property<String> userName = new Property<String>("userName", "User Name", String.class);
Property<String> displayName = new Property<String>("displayName", "Display Name",
String.class);
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/EmployeeRequest.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/EmployeeRequest.java
index 65a762a..8952ada 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/EmployeeRequest.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/EmployeeRequest.java
@@ -18,7 +18,8 @@
import com.google.gwt.requestfactory.shared.RecordListRequest;
import com.google.gwt.requestfactory.shared.RecordRequest;
import com.google.gwt.requestfactory.shared.RequestFactory;
-import com.google.gwt.requestfactory.shared.ServerOperation;
+import com.google.gwt.requestfactory.shared.Service;
+import com.google.gwt.sample.expenses.server.domain.Employee;
import com.google.gwt.valuestore.shared.PropertyReference;
/**
@@ -28,137 +29,39 @@
* <p>
* IRL this class will be generated by a JPA-savvy tool run before compilation.
*/
+@Service(Employee.class)
public interface EmployeeRequest {
/**
- * Defines the server operations that handle these requests.
- */
- public enum ServerOperations implements RequestFactory.RequestDefinition {
- COUNT_EMPLOYEES {
- public String getDomainMethodName() {
- return "countEmployees";
- }
-
- public Class<?> getReturnType() {
- return Long.class;
- }
-
- public boolean isReturnTypeList() {
- return false;
- }
- },
-
- COUNT_EMPLOYEES_BY_DEPARTMENT {
- public String getDomainMethodName() {
- return "countEmployeesByDepartment";
- }
-
- public Class<?>[] getParameterTypes() {
- return new Class[]{java.lang.String.class};
- }
-
- public Class<?> getReturnType() {
- return Long.class;
- }
-
- public boolean isReturnTypeList() {
- return false;
- }
- },
-
- FIND_ALL_EMPLOYEES {
- public String getDomainMethodName() {
- return "findAllEmployees";
- }
- },
-
- FIND_EMPLOYEE {
- public String getDomainMethodName() {
- return "findEmployee";
- }
-
- public Class<?>[] getParameterTypes() {
- return new Class[] {Long.class};
- }
-
- public boolean isReturnTypeList() {
- return false;
- }
- },
-
- FIND_EMPLOYEE_ENTRIES {
- public String getDomainMethodName() {
- return "findEmployeeEntries";
- }
-
- public Class<?>[] getParameterTypes() {
- return new Class[]{int.class, int.class};
- }
- },
-
- FIND_EMPLOYEE_ENTRIES_BY_DEPARTMENT {
- public String getDomainMethodName() {
- return "findEmployeeEntriesByDepartment";
- }
-
- public Class<?>[] getParameterTypes() {
- return new Class[]{java.lang.String.class, int.class, int.class};
- }
- };
-
- public String getDomainClassName() {
- return "com.google.gwt.sample.expenses.server.domain.Employee";
- }
-
- public Class<?>[] getParameterTypes() {
- return null;
- }
-
- public Class<?> getReturnType() {
- return EmployeeRecord.class;
- }
-
- public boolean isReturnTypeList() {
- return true;
- }
- }
-
- /**
* @return a request object
*/
- @ServerOperation("COUNT_EMPLOYEES")
RequestFactory.RequestObject<Long> countEmployees();
/**
* @return a request object
*/
- @ServerOperation("COUNT_EMPLOYEES_BY_DEPARTMENT")
RequestFactory.RequestObject<Long> countEmployeesByDepartment(
String department);
/**
* @return a request object
*/
- @ServerOperation("FIND_ALL_EMPLOYEES")
RecordListRequest<EmployeeRecord> findAllEmployees();
/**
* @return a request object
*/
- @ServerOperation("FIND_EMPLOYEE")
RecordRequest<EmployeeRecord> findEmployee(PropertyReference<String> id);
/**
* @return a request object
*/
- @ServerOperation("FIND_EMPLOYEE_ENTRIES")
RecordListRequest<EmployeeRecord> findEmployeeEntries(int firstResult,
int maxResults);
/**
* @return a request object
*/
- @ServerOperation("FIND_EMPLOYEE_ENTRIES_BY_DEPARTMENT")
RecordListRequest<EmployeeRecord> findEmployeeEntriesByDepartment(
String department, int firstResult, int maxResults);
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ExpenseRecord.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ExpenseRecord.java
index 0c83865..e9c2b74 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ExpenseRecord.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ExpenseRecord.java
@@ -15,7 +15,7 @@
*/
package com.google.gwt.sample.expenses.gwt.request;
-import com.google.gwt.requestfactory.shared.ServerType;
+import com.google.gwt.requestfactory.shared.DataTransferObject;
import com.google.gwt.valuestore.shared.Property;
import com.google.gwt.valuestore.shared.Record;
@@ -27,15 +27,9 @@
* <p>
* IRL this class will be generated by a JPA-savvy tool run before compilation.
*/
-@ServerType(type = com.google.gwt.sample.expenses.server.domain.Expense.class)
+@DataTransferObject(com.google.gwt.sample.expenses.server.domain.Expense.class)
public interface ExpenseRecord extends Record {
- /**
- * Used as input to {@link com.google.gwt.valuestore.shared.DeltaValueStore#create(Record)
- * DeltaValueStore#create()} and in the wire format during sync requests.
- */
- String TOKEN = "ExpenseRecord";
-
Property<Double> amount = new Property<Double>("amount", "Amount", Double.class);
Property<String> approval = new Property<String>("approval", "Approval", String.class);
Property<String> category = new Property<String>("category", "Category", String.class);
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ExpenseRequest.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ExpenseRequest.java
index 4cce650..62216f9 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ExpenseRequest.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ExpenseRequest.java
@@ -16,10 +16,9 @@
package com.google.gwt.sample.expenses.gwt.request;
import com.google.gwt.requestfactory.shared.RecordListRequest;
-import com.google.gwt.requestfactory.shared.RequestFactory;
-import com.google.gwt.requestfactory.shared.ServerOperation;
+import com.google.gwt.requestfactory.shared.Service;
+import com.google.gwt.sample.expenses.server.domain.Expense;
import com.google.gwt.valuestore.shared.PropertyReference;
-import com.google.gwt.valuestore.shared.Record;
/**
* "API Generated" request selector interface implemented by objects that give
@@ -28,72 +27,23 @@
* <p>
* IRL this class will be generated by a JPA-savvy tool run before compilation.
*/
+@Service(Expense.class)
public interface ExpenseRequest {
/**
- * Defines the server operations that handle these requests.
- */
- public enum ServerOperations implements RequestFactory.RequestDefinition {
- FIND_ALL_EXPENSES {
- public String getDomainMethodName() {
- return "findAllExpenses";
- }
- },
-
- FIND_EXPENSE {
- public String getDomainMethodName() {
- return "findListOfOneExpense";
- }
-
- public Class<?>[] getParameterTypes() {
- return new Class[] {java.lang.Long.class};
- }
- },
-
- FIND_EXPENSES_BY_REPORT {
- public String getDomainMethodName() {
- return "findExpensesByReport";
- }
-
- public Class<?>[] getParameterTypes() {
- return new Class[] {java.lang.Long.class};
- }
- };
-
- public String getDomainClassName() {
- return "com.google.gwt.sample.expenses.server.domain.Expense";
- }
-
- public Class<?>[] getParameterTypes() {
- return null;
- }
-
- public Class<? extends Record> getReturnType() {
- return ExpenseRecord.class;
- }
-
- public boolean isReturnTypeList() {
- return true;
- }
- }
-
- /**
* @return a request object
*/
- @ServerOperation("FIND_ALL_EXPENSES")
RecordListRequest<ExpenseRecord> findAllExpenses();
/**
* @return a request object
*/
- @ServerOperation("FIND_EXPENSE")
RecordListRequest<ExpenseRecord> findExpense(
PropertyReference<String> id);
/**
* @return a request object
*/
- @ServerOperation("FIND_EXPENSES_BY_REPORT")
RecordListRequest<ExpenseRecord> findExpensesByReport(
PropertyReference<String> reportId);
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ExpensesServerSideOperations.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ExpensesServerSideOperations.java
deleted file mode 100644
index ab85505..0000000
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ExpensesServerSideOperations.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.gwt.sample.expenses.gwt.request;
-
-import com.google.gwt.requestfactory.shared.RequestFactory.Config;
-import com.google.gwt.requestfactory.shared.RequestFactory.RequestDefinition;
-import com.google.gwt.valuestore.shared.Record;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * "API Generated" configuration class for
- * {@link com.google.gwt.requestfactory.server.RequestFactoryServlet
- * RequestFactoryServlet}.
- * <p>
- * IRL this class will be generated by a JPA-savvy tool run before compilation.
- */
-public class ExpensesServerSideOperations implements Config {
-
- private static void putAll(RequestDefinition[] values,
- Map<String, RequestDefinition> newMap) {
- for (RequestDefinition def : values) {
- newMap.put(def.name(), def);
- }
- }
-
- private final Map<String, RequestDefinition> map;
-
- public ExpensesServerSideOperations() {
- Map<String, RequestDefinition> newMap = new HashMap<String, RequestDefinition>();
- putAll(EmployeeRequest.ServerOperations.values(), newMap);
- putAll(ReportRequest.ServerOperations.values(), newMap);
- putAll(ExpenseRequest.ServerOperations.values(), newMap);
- map = Collections.unmodifiableMap(newMap);
- }
-
- public Set<Class<? extends Record>> recordTypes() {
- Set<Class<? extends Record>> records = new HashSet<Class<? extends Record>>();
- records.add(EmployeeRecord.class);
- records.add(ReportRecord.class);
- records.add(ExpenseRecord.class);
- return records;
- }
-
- public Map<String, RequestDefinition> requestDefinitions() {
- return map;
- }
-
-}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ReportRecord.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ReportRecord.java
index 7fce0cf..d93ed30 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ReportRecord.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ReportRecord.java
@@ -15,7 +15,7 @@
*/
package com.google.gwt.sample.expenses.gwt.request;
-import com.google.gwt.requestfactory.shared.ServerType;
+import com.google.gwt.requestfactory.shared.DataTransferObject;
import com.google.gwt.valuestore.shared.Property;
import com.google.gwt.valuestore.shared.Record;
@@ -27,15 +27,9 @@
* <p>
* IRL this class will be generated by a JPA-savvy tool run before compilation.
*/
-@ServerType(type = com.google.gwt.sample.expenses.server.domain.Report.class)
+@DataTransferObject(com.google.gwt.sample.expenses.server.domain.Report.class)
public interface ReportRecord extends Record {
- /**
- * Used as input to {@link com.google.gwt.valuestore.shared.DeltaValueStore#create(Record)
- * DeltaValueStore#create()} and in the wire format during sync requests.
- */
- String TOKEN = "ReportRecord";
-
Property<String> approvedSupervisorKey = new Property<String>("approvedSupervisorKey", "Approved Supervisor Key",
String.class);
Property<Date> created = new Property<Date>("created", "Created", Date.class);
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ReportRequest.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ReportRequest.java
index 77fb2c1..2a807b1 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ReportRequest.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ReportRequest.java
@@ -18,7 +18,8 @@
import com.google.gwt.requestfactory.shared.RecordListRequest;
import com.google.gwt.requestfactory.shared.RecordRequest;
import com.google.gwt.requestfactory.shared.RequestFactory;
-import com.google.gwt.requestfactory.shared.ServerOperation;
+import com.google.gwt.requestfactory.shared.Service;
+import com.google.gwt.sample.expenses.server.domain.Report;
import com.google.gwt.valuestore.shared.PropertyReference;
/**
@@ -28,150 +29,38 @@
* <p>
* IRL this class will be generated by a JPA-savvy tool run before compilation.
*/
+@Service(Report.class)
public interface ReportRequest {
/**
- * Defines the server operations that handle these requests.
- */
- public enum ServerOperations implements RequestFactory.RequestDefinition {
- COUNT_REPORTS {
- public String getDomainMethodName() {
- return "countReports";
- }
-
- public Class<?> getReturnType() {
- return long.class;
- }
-
- public boolean isReturnTypeList() {
- return false;
- }
- },
-
- COUNT_REPORTS_BY_SEARCH {
- public String getDomainMethodName() {
- return "countReportsBySearch";
- }
-
- public Class<?>[] getParameterTypes() {
- return new Class[] {java.lang.Long.class, java.lang.String.class,
- java.lang.String.class};
- }
-
- public Class<?> getReturnType() {
- return long.class;
- }
-
- public boolean isReturnTypeList() {
- return false;
- }
- },
-
- FIND_ALL_REPORTS {
- public String getDomainMethodName() {
- return "findAllReports";
- }
- },
-
- FIND_REPORT {
- public String getDomainMethodName() {
- return "findReport";
- }
-
- public Class<?>[] getParameterTypes() {
- return new Class[] {java.lang.Long.class};
- }
-
- public boolean isReturnTypeList() {
- return false;
- }
- },
-
- FIND_REPORTS_BY_EMPLOYEE {
- public String getDomainMethodName() {
- return "findReportsByEmployee";
- }
-
- public Class<?>[] getParameterTypes() {
- return new Class[] {java.lang.Long.class};
- }
- },
-
- FIND_REPORT_ENTRIES {
- public String getDomainMethodName() {
- return "findReportEntries";
- }
-
- public Class<?>[] getParameterTypes() {
- return new Class[] {int.class, int.class};
- }
- },
-
- FIND_REPORT_ENTRIES_BY_SEARCH {
- public String getDomainMethodName() {
- return "findReportEntriesBySearch";
- }
-
- public Class<?>[] getParameterTypes() {
- return new Class[]{
- java.lang.Long.class, java.lang.String.class,
- java.lang.String.class, java.lang.String.class, int.class,
- int.class};
- }
- };
-
- public String getDomainClassName() {
- return "com.google.gwt.sample.expenses.server.domain.Report";
- }
-
- public Class<?>[] getParameterTypes() {
- return null;
- }
-
- public Class<?> getReturnType() {
- return ReportRecord.class;
- }
-
- public boolean isReturnTypeList() {
- return true;
- }
- }
-
- /**
* @return a request object
*/
- @ServerOperation("COUNT_REPORTS")
RequestFactory.RequestObject<Long> countReports();
/**
* @return a request object
*/
- @ServerOperation("COUNT_REPORTS_BY_SEARCH")
RequestFactory.RequestObject<Long> countReportsBySearch(Long employeeId,
String department, String startsWith);
/**
* @return a request object
*/
- @ServerOperation("FIND_ALL_REPORTS")
RecordListRequest<ReportRecord> findAllReports();
/**
* @return a request object
*/
- @ServerOperation("FIND_REPORT")
RecordRequest<ReportRecord> findReport(PropertyReference<String> id);
/**
* @return a request object
*/
- @ServerOperation("FIND_REPORT_ENTRIES")
RecordListRequest<ReportRecord> findReportEntries(int firstResult, int maxResults);
/**
* @return a request object
*/
- @ServerOperation("FIND_REPORT_ENTRIES_BY_SEARCH")
RecordListRequest<ReportRecord> findReportEntriesBySearch(Long employeeId,
String department, String startsWith, String orderBy, int firstResult,
int maxResults);
@@ -179,7 +68,6 @@
/**
* @return a request object
*/
- @ServerOperation("FIND_REPORTS_BY_EMPLOYEE")
RecordListRequest<ReportRecord> findReportsByEmployee(
PropertyReference<String> id);
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/employee/EmployeeEditActivity.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/employee/EmployeeEditActivity.java
index 509ca40..16a59b5 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/employee/EmployeeEditActivity.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/employee/EmployeeEditActivity.java
@@ -75,7 +75,7 @@
}
@Override
- protected String getRecordToken() {
- return EmployeeRecord.TOKEN;
+ protected Class getRecordClass() {
+ return EmployeeRecord.class;
}
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/report/ReportEditActivity.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/report/ReportEditActivity.java
index 9fa5505..0898b19 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/report/ReportEditActivity.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/report/ReportEditActivity.java
@@ -74,7 +74,7 @@
}
@Override
- protected String getRecordToken() {
- return ReportRecord.TOKEN;
+ protected Class getRecordClass() {
+ return ReportRecord.class;
}
}
diff --git a/bikeshed/src/com/google/gwt/valuestore/client/DeltaValueStoreJsonImpl.java b/bikeshed/src/com/google/gwt/valuestore/client/DeltaValueStoreJsonImpl.java
index 30c6af8..4ab7e84 100644
--- a/bikeshed/src/com/google/gwt/valuestore/client/DeltaValueStoreJsonImpl.java
+++ b/bikeshed/src/com/google/gwt/valuestore/client/DeltaValueStoreJsonImpl.java
@@ -260,7 +260,7 @@
return syncResults;
}
- public Record create(String token) {
+ public Record create(Class token) {
if (used) {
throw new IllegalStateException(
"create can only be called on an un-used DeltaValueStore");
@@ -456,7 +456,7 @@
} else {
requestData.append(",");
}
- requestData.append("{\"" + entry.getValue().getSchema().getToken()
+ requestData.append("{\"" + entry.getValue().getSchema().getToken().getName()
+ "\":");
if (writeOperation != WriteOperation.DELETE) {
requestData.append(impl.toJson());
diff --git a/bikeshed/src/com/google/gwt/valuestore/shared/DeltaValueStore.java b/bikeshed/src/com/google/gwt/valuestore/shared/DeltaValueStore.java
index d7df718..65d6f01 100644
--- a/bikeshed/src/com/google/gwt/valuestore/shared/DeltaValueStore.java
+++ b/bikeshed/src/com/google/gwt/valuestore/shared/DeltaValueStore.java
@@ -31,7 +31,7 @@
*/
void clearUsed();
- Record create(String token);
+ Record create(Class token);
void delete(Record record);
diff --git a/bikeshed/src/com/google/gwt/valuestore/shared/impl/RecordSchema.java b/bikeshed/src/com/google/gwt/valuestore/shared/impl/RecordSchema.java
index 2f9d8c0..24f12e6 100644
--- a/bikeshed/src/com/google/gwt/valuestore/shared/impl/RecordSchema.java
+++ b/bikeshed/src/com/google/gwt/valuestore/shared/impl/RecordSchema.java
@@ -62,6 +62,6 @@
return createChangeEvent(record, writeOperation);
}
- public abstract String getToken();
+ public abstract Class<? extends Record> getToken();
}
\ No newline at end of file
diff --git a/bikeshed/src/com/google/gwt/valuestore/shared/impl/RecordToTypeMap.java b/bikeshed/src/com/google/gwt/valuestore/shared/impl/RecordToTypeMap.java
index 09b0c1d..46b5725 100644
--- a/bikeshed/src/com/google/gwt/valuestore/shared/impl/RecordToTypeMap.java
+++ b/bikeshed/src/com/google/gwt/valuestore/shared/impl/RecordToTypeMap.java
@@ -27,5 +27,5 @@
* Record class to its internal "type" representation.
*/
public interface RecordToTypeMap {
- RecordSchema<? extends Record> getType(String token);
+ RecordSchema<? extends Record> getType(Class<? extends Record> recordClass);
}
diff --git a/bikeshed/test/com/google/gwt/requestfactory/server/ReflectionBasedOperationRegistryTest.java b/bikeshed/test/com/google/gwt/requestfactory/server/ReflectionBasedOperationRegistryTest.java
new file mode 100644
index 0000000..2b5577c
--- /dev/null
+++ b/bikeshed/test/com/google/gwt/requestfactory/server/ReflectionBasedOperationRegistryTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.gwt.requestfactory.server;
+
+import com.google.gwt.requestfactory.shared.RequestFactory;
+import com.google.gwt.valuestore.shared.SimpleFooRecord;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for
+ * {@link com.google.gwt.requestfactory.server.ReflectionBasedOperationRegistry}
+ * .
+ */
+public class ReflectionBasedOperationRegistryTest extends TestCase {
+
+ private ReflectionBasedOperationRegistry registry;
+
+ public void setUp() {
+ registry = new ReflectionBasedOperationRegistry(
+ new DefaultSecurityProvider());
+ }
+
+ public void testGetOperationListNoArgs() {
+ RequestFactory.RequestDefinition request = registry.getOperation("com.google.gwt.requestfactory.server.SimpleFooRequest::findAll");
+ assert request != null;
+ assertEquals("com.google.gwt.requestfactory.server.SimpleFoo",
+ request.getDomainClassName());
+ assertEquals("findAll", request.getDomainMethodName());
+ assertEquals(SimpleFooRecord.class, request.getReturnType());
+ assertEquals(0, request.getParameterTypes().length);
+ assertEquals(true, request.isReturnTypeList());
+ }
+
+ public void testGetOperationScalarNoArgs() {
+ RequestFactory.RequestDefinition request = registry.getOperation("com.google.gwt.requestfactory.server.SimpleFooRequest::countSimpleFoo");
+ assert request != null;
+ assertEquals("com.google.gwt.requestfactory.server.SimpleFoo",
+ request.getDomainClassName());
+ assertEquals("countSimpleFoo", request.getDomainMethodName());
+ assertEquals(Long.class, request.getReturnType());
+ assertEquals(0, request.getParameterTypes().length);
+ assertEquals(false, request.isReturnTypeList());
+ }
+
+ public void testGetOpertionScalarWithArgs() {
+ RequestFactory.RequestDefinition request = registry.getOperation("com.google.gwt.requestfactory.server.SimpleFooRequest::findSimpleFooById");
+ assert request != null;
+ assertEquals("com.google.gwt.requestfactory.server.SimpleFoo",
+ request.getDomainClassName());
+ assertEquals("findSimpleFooById", request.getDomainMethodName());
+ assertEquals(SimpleFooRecord.class, request.getReturnType());
+ assert request.getParameterTypes().length == 1
+ && request.getParameterTypes()[0] == Long.class;
+ assertEquals(false, request.isReturnTypeList());
+ }
+
+ public void testInsecureOperations() {
+ try {
+ // bogus class
+ registry.getOperation("com.foo.Foo::bar");
+ fail("Access to non-existent class.");
+ } catch (SecurityException se) {
+ // expected
+ }
+ try {
+ // no @Service
+ registry.getOperation("java.lang.System::currentTimeMillis");
+ fail("Access allowed to class without @Service annotation");
+ } catch (SecurityException se) {
+ // expected
+ }
+ }
+
+ public void testPrivateMethodFails() {
+ RequestFactory.RequestDefinition request = registry.getOperation("com.google.gwt.requestfactory.server.SimpleFooRequest::privateMethod");
+ assert request == null;
+ }
+}
diff --git a/bikeshed/test/com/google/gwt/requestfactory/server/SimpleFoo.java b/bikeshed/test/com/google/gwt/requestfactory/server/SimpleFoo.java
new file mode 100644
index 0000000..7a6c504
--- /dev/null
+++ b/bikeshed/test/com/google/gwt/requestfactory/server/SimpleFoo.java
@@ -0,0 +1,40 @@
+/*
+ * 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.gwt.requestfactory.server;
+
+import java.util.List;
+
+/**
+ * Domain object for SimpleFooRequest.
+ */
+public class SimpleFoo {
+ public static Long countSimpleFoo() {
+ return 0L;
+ }
+
+ public static List<SimpleFoo> findAll() {
+ return null;
+ }
+
+ public static SimpleFoo findSimpleFooById(Long id) {
+ return null;
+ }
+
+ @SuppressWarnings("unused")
+ private static Integer privateMethod() {
+ return 0;
+ }
+}
diff --git a/bikeshed/test/com/google/gwt/requestfactory/server/SimpleFooRequest.java b/bikeshed/test/com/google/gwt/requestfactory/server/SimpleFooRequest.java
new file mode 100644
index 0000000..87300b2
--- /dev/null
+++ b/bikeshed/test/com/google/gwt/requestfactory/server/SimpleFooRequest.java
@@ -0,0 +1,32 @@
+/*
+ * 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.gwt.requestfactory.server;
+
+import com.google.gwt.requestfactory.shared.RecordListRequest;
+import com.google.gwt.requestfactory.shared.RequestFactory;
+import com.google.gwt.requestfactory.shared.Service;
+import com.google.gwt.valuestore.shared.SimpleFooRecord;
+
+/**
+ * Do nothing test interface.
+ */
+@Service(com.google.gwt.requestfactory.server.SimpleFoo.class)
+public interface SimpleFooRequest {
+ RequestFactory.RequestObject<Long> countSimpleFoo();
+ RecordListRequest<SimpleFooRecord> findAll();
+ RequestFactory.RequestObject<SimpleFooRecord> findSimpleFooById(Long id);
+ RequestFactory.RequestObject<Integer> privateMethod();
+}
diff --git a/bikeshed/test/com/google/gwt/valuestore/client/DeltaValueStoreJsonImplTest.java b/bikeshed/test/com/google/gwt/valuestore/client/DeltaValueStoreJsonImplTest.java
index e766120..651c447 100644
--- a/bikeshed/test/com/google/gwt/valuestore/client/DeltaValueStoreJsonImplTest.java
+++ b/bikeshed/test/com/google/gwt/valuestore/client/DeltaValueStoreJsonImplTest.java
@@ -58,11 +58,12 @@
@Override
public void gwtSetUp() {
valueStore = new ValueStoreJsonImpl(null, new RecordToTypeMap() {
- public RecordSchema<? extends Record> getType(String token) {
- if (token.equals(SimpleFooRecordImpl.TOKEN)) {
+ public RecordSchema<? extends Record> getType(Class<? extends Record>
+ recordClass) {
+ if (recordClass.equals(SimpleFooRecord.class)) {
return SimpleFooRecordImpl.SCHEMA;
}
- throw new IllegalArgumentException("Unknown token " + token);
+ throw new IllegalArgumentException("Unknown token " + recordClass);
}
});
@@ -81,7 +82,7 @@
public void testCreate() {
DeltaValueStoreJsonImpl deltaValueStore = valueStore.spawnDeltaView();
- Record created = deltaValueStore.create(SimpleFooRecordImpl.TOKEN);
+ Record created = deltaValueStore.create(SimpleFooRecord.class);
assertNotNull(created.getId());
assertNotNull(created.getVersion());
@@ -91,7 +92,7 @@
public void testCreateDelete() {
DeltaValueStoreJsonImpl deltaValueStore = valueStore.spawnDeltaView();
- Record created = deltaValueStore.create(SimpleFooRecordImpl.TOKEN);
+ Record created = deltaValueStore.create(SimpleFooRecord.class);
assertTrue(deltaValueStore.isChanged());
deltaValueStore.delete(created);
assertFalse(deltaValueStore.isChanged());
@@ -102,7 +103,7 @@
public void testCreateUpdate() {
DeltaValueStoreJsonImpl deltaValueStore = valueStore.spawnDeltaView();
- Record created = deltaValueStore.create(SimpleFooRecordImpl.TOKEN);
+ Record created = deltaValueStore.create(SimpleFooRecord.class);
assertTrue(deltaValueStore.isChanged());
deltaValueStore.set(SimpleFooRecord.userName, created, "harry");
assertTrue(deltaValueStore.isChanged());
@@ -202,9 +203,9 @@
JSONObject recordWithName = writeOperationArray.get(0).isObject();
assertEquals(1, recordWithName.size());
- assertTrue(recordWithName.containsKey(SimpleFooRecordImpl.TOKEN));
+ assertTrue(recordWithName.containsKey(SimpleFooRecord.class.getName()));
- JSONObject record = recordWithName.get(SimpleFooRecordImpl.TOKEN).isObject();
+ JSONObject record = recordWithName.get(SimpleFooRecord.class.getName()).isObject();
assertTrue(record.containsKey("id"));
assertTrue(record.containsKey("version"));
diff --git a/bikeshed/test/com/google/gwt/valuestore/shared/SimpleFooRecord.java b/bikeshed/test/com/google/gwt/valuestore/shared/SimpleFooRecord.java
index c9b459d..0061fc0 100644
--- a/bikeshed/test/com/google/gwt/valuestore/shared/SimpleFooRecord.java
+++ b/bikeshed/test/com/google/gwt/valuestore/shared/SimpleFooRecord.java
@@ -15,12 +15,16 @@
*/
package com.google.gwt.valuestore.shared;
+import com.google.gwt.requestfactory.server.SimpleFoo;
+import com.google.gwt.requestfactory.shared.DataTransferObject;
+
import java.util.Date;
/**
* A simple entity used for testing. Has an int field and date field. Add other
* data types as their support gets built in.
*/
+@DataTransferObject(SimpleFoo.class)
public interface SimpleFooRecord extends Record {
String TOKEN = "SimpleFooRecord";
diff --git a/bikeshed/test/com/google/gwt/valuestore/shared/impl/SimpleFooRecordImpl.java b/bikeshed/test/com/google/gwt/valuestore/shared/impl/SimpleFooRecordImpl.java
index 122b485..342e4ac 100644
--- a/bikeshed/test/com/google/gwt/valuestore/shared/impl/SimpleFooRecordImpl.java
+++ b/bikeshed/test/com/google/gwt/valuestore/shared/impl/SimpleFooRecordImpl.java
@@ -64,8 +64,8 @@
return null;
}
- public String getToken() {
- return SimpleFooRecord.TOKEN; // special field
+ public Class<? extends Record> getToken() {
+ return SimpleFooRecord.class; // special field
}
}
diff --git a/bikeshed/war/WEB-INF/web.xml b/bikeshed/war/WEB-INF/web.xml
index 572ccb8..71aea7d 100644
--- a/bikeshed/war/WEB-INF/web.xml
+++ b/bikeshed/war/WEB-INF/web.xml
@@ -5,16 +5,10 @@
<web-app>
- <!-- Configures expensesData servlet -->
- <context-param>
- <param-name>servlet.serverOperation</param-name>
- <param-value>com.google.gwt.sample.expenses.gwt.request.ExpensesServerSideOperations</param-value>
- </context-param>
-
<!-- Servlets -->
<servlet>
<servlet-name>requestFactoryServlet</servlet-name>
- <servlet-class> com.google.gwt.requestfactory.server.RequestFactoryServlet</servlet-class>
+ <servlet-class>com.google.gwt.requestfactory.server.RequestFactoryServlet</servlet-class>
</servlet>
<servlet>