Author: lryan
Date: Wed Feb 11 00:44:59 2009
New Revision: 743183
URL: http://svn.apache.org/viewvc?rev=743183&view=rev
Log:
Better path matching for REST operation dispatch. Now matches paths with the
greater no. of constant parts first.
Add support for overriding the operation name on RPC calls
Simplify the interface to RestHandler and RpcHandler.
Modified:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/DataServiceServlet.java
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/DefaultHandlerRegistry.java
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/JsonRpcServlet.java
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/Operation.java
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/RestHandler.java
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/RpcHandler.java
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/DefaultHandlerRegistryTest.java
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/TestHandler.java
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/ActivityHandlerTest.java
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/AppDataHandlerTest.java
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/PersonHandlerTest.java
Modified:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/DataServiceServlet.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/DataServiceServlet.java?rev=743183&r1=743182&r2=743183&view=diff
==============================================================================
---
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/DataServiceServlet.java
(original)
+++
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/DataServiceServlet.java
Wed Feb 11 00:44:59 2009
@@ -22,9 +22,6 @@
import com.google.common.collect.ImmutableMap;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
@@ -32,6 +29,10 @@
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
public class DataServiceServlet extends ApiServlet {
protected static final String FORMAT_PARAM = "format";
@@ -119,8 +120,7 @@
}
// Execute the request
- Future future = handler.execute(path, servletRequest.getParameterMap(),
- bodyReader, token, converter);
+ Future future = handler.execute(servletRequest.getParameterMap(),
bodyReader, token, converter);
ResponseItem responseItem = getResponseItem(future);
Modified:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/DefaultHandlerRegistry.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/DefaultHandlerRegistry.java?rev=743183&r1=743182&r2=743183&view=diff
==============================================================================
---
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/DefaultHandlerRegistry.java
(original)
+++
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/DefaultHandlerRegistry.java
Wed Feb 11 00:44:59 2009
@@ -25,10 +25,13 @@
import org.apache.shindig.protocol.conversion.BeanConverter;
import org.apache.shindig.protocol.conversion.BeanJsonConverter;
+import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.name.Named;
+
import org.json.JSONException;
import org.json.JSONObject;
@@ -37,10 +40,10 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.SortedSet;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -53,21 +56,23 @@
private static final Logger logger =
Logger.getLogger(DefaultHandlerRegistry.class.getName());
- private final Map<String, RestHandler> restOperations = Maps.newHashMap();
- private final Map<String, RpcHandler> rpcOperations = Maps.newHashMap();
+ // Map service - > method -> { handler, ...}
+ private final Map<String, Map<String, SortedSet<RestPath>>>
serviceMethodPathMap = Maps.newHashMap();
+ private final Map<String, RpcInvocationHandler> rpcOperations =
Maps.newHashMap();
private final Injector injector;
private final BeanJsonConverter beanJsonConverter;
/**
* Creates a dispatcher with the specified handler classes
+ *
* @param injector Used to create instance if handler is a Class
* @param handlers List of handler instances or classes.
*/
@Inject
public DefaultHandlerRegistry(Injector injector,
- @Named("org.apache.shindig.handlers") List handlers,
- BeanJsonConverter beanJsonConverter) {
+ @Named("org.apache.shindig.handlers")List
handlers,
+ BeanJsonConverter beanJsonConverter) {
this.injector = injector;
this.beanJsonConverter = beanJsonConverter;
addHandlers(handlers.toArray());
@@ -79,15 +84,15 @@
public RpcHandler getRpcHandler(JSONObject rpc) {
try {
String key = rpc.getString("method");
- RpcHandler rpcHandler = rpcOperations.get(key);
+ RpcInvocationHandler rpcHandler = rpcOperations.get(key);
if (rpcHandler == null) {
return new ErrorRpcHandler(new
ProtocolException(ResponseError.NOT_IMPLEMENTED,
"The method " + key + " is not implemented"));
}
- return rpcHandler;
+ return new RpcInvocationWrapper(rpcHandler, rpc);
} catch (JSONException je) {
return new ErrorRpcHandler(new
ProtocolException(ResponseError.BAD_REQUEST,
- "No method requested in RPC"));
+ "No method requested in RPC"));
}
}
@@ -96,23 +101,34 @@
*/
public RestHandler getRestHandler(String path, String method) {
method = method.toUpperCase();
- RestHandler restHandler = null;
- String matchPath = path;
- int separatorIndex = matchPath.lastIndexOf("/");
- while (restHandler == null && separatorIndex != -1) {
- restHandler = restOperations.get(method + " " + matchPath);
- matchPath = matchPath.substring(0, separatorIndex);
- separatorIndex = matchPath.lastIndexOf("/");
- }
- if (restHandler == null) {
- return new ErrorRestHandler(new
ProtocolException(ResponseError.NOT_IMPLEMENTED,
- "No service defined for path " + path));
+ if (path.startsWith("/")) {
+ path = path.substring(1);
+ }
+ String[] pathParts = path.split("/");
+ Map<String, SortedSet<RestPath>> methods =
serviceMethodPathMap.get(pathParts[0]);
+ if (methods != null) {
+ SortedSet<RestPath> paths = methods.get(method);
+ for (RestPath restPath : paths) {
+ RestHandler handler = restPath.accept(pathParts);
+ if (handler != null) {
+ return handler;
+ }
+ }
}
- return restHandler;
+ return new ErrorRestHandler(new
ProtocolException(ResponseError.NOT_IMPLEMENTED,
+ "No service defined for path " + path));
}
public Set<String> getSupportedRestServices() {
- return Collections.unmodifiableSet(restOperations.keySet());
+ Set<String> result = Sets.newTreeSet();
+ for (Map<String, SortedSet<RestPath>> methods :
serviceMethodPathMap.values()) {
+ for (String method : methods.keySet()) {
+ for (RestPath path : methods.get(method)) {
+ result.add(method + " " + path.operationPath);
+ }
+ }
+ }
+ return Collections.unmodifiableSet(result);
}
public Set<String> getSupportedRpcServices() {
@@ -141,7 +157,7 @@
for (Method m : handlerType.getMethods()) {
if (m.isAnnotationPresent(Operation.class)) {
Operation op = m.getAnnotation(Operation.class);
- createRpcHandler(handler, service, m);
+ createRpcHandler(handler, service, op, m);
createRestHandler(handler, service, op, m);
}
}
@@ -154,25 +170,35 @@
try {
if (RequestItem.class.isAssignableFrom(requestItemType)) {
if (requestItemType == RequestItem.class) {
- requestItemType = BaseRequestItem.class;
+ requestItemType = BaseRequestItem.class;
}
Constructor reqItemConstructor =
requestItemType.getConstructor(Map.class, SecurityToken.class,
BeanConverter.class,
BeanJsonConverter.class);
- RestHandler restHandler = new RestInvocationHandler(service, op, m,
handler,
+ RestInvocationHandler restHandler = new RestInvocationHandler(service,
op, m, handler,
beanJsonConverter, reqItemConstructor);
String serviceName = service.name();
+ Map<String, SortedSet<RestPath>> methods =
serviceMethodPathMap.get(serviceName);
+ if (methods == null) {
+ methods = Maps.newHashMap();
+ serviceMethodPathMap.put(serviceName, methods);
+ }
+
for (String httpMethod : op.httpMethods()) {
if (!StringUtils.isEmpty(httpMethod)) {
+ httpMethod = httpMethod.toUpperCase();
+ SortedSet<RestPath> sortedSet = methods.get(httpMethod);
+ if (sortedSet == null) {
+ sortedSet = Sets.newTreeSet();
+ methods.put(httpMethod, sortedSet);
+ }
+
if (StringUtils.isEmpty(op.path())) {
- // Use the standard service name as the key
- restOperations.put(httpMethod.toUpperCase() + " /" +
serviceName, restHandler);
+ sortedSet.add(new RestPath("/" + serviceName + service.path(),
restHandler));
} else {
// Use the standard service name and constant prefix as the key
- String prefix = op.path().split("\\{")[0];
- restOperations.put(httpMethod.toUpperCase() + " /" + serviceName
+
- prefix, restHandler);
+ sortedSet.add(new RestPath("/" + serviceName + op.path(),
restHandler));
}
}
}
@@ -183,7 +209,7 @@
}
- private void createRpcHandler(Object handler, Service service, Method m) {
+ private void createRpcHandler(Object handler, Service service, Operation op,
Method m) {
// Check for request item subclass constructor
Class requestItemType = m.getParameterTypes()[0];
try {
@@ -195,8 +221,13 @@
requestItemType.getConstructor(JSONObject.class,
SecurityToken.class,
BeanConverter.class,
BeanJsonConverter.class);
- RpcHandler rpcHandler = new RpcInvocationHandler(m, handler,
beanJsonConverter, reqItemConstructor);
+ RpcInvocationHandler rpcHandler =
+ new RpcInvocationHandler(m, handler, beanJsonConverter,
reqItemConstructor);
String defaultName = service.name() + "." + m.getName();
+ // Use the override if its defined
+ if (op.name().length() > 0) {
+ defaultName = service.name() + "." + op.name();
+ }
rpcOperations.put(defaultName, rpcHandler);
}
} catch (NoSuchMethodException nme) {
@@ -208,7 +239,7 @@
* Proxy binding for an RPC operation. We allow binding to methods that
* return non-Future types by wrapping them in ImmediateFuture.
*/
- private static final class RpcInvocationHandler implements RpcHandler {
+ static final class RpcInvocationHandler {
private Method receiver;
Object handlerInstance;
BeanJsonConverter beanJsonConverter;
@@ -218,7 +249,7 @@
BeanJsonConverter beanJsonConverter,
Constructor reqItemConstructor) {
this.receiver = receiver;
- this.handlerInstance = handlerInstance;
+ this.handlerInstance = handlerInstance;
this.beanJsonConverter = beanJsonConverter;
this.requestItemConstructor = reqItemConstructor;
}
@@ -227,15 +258,15 @@
try {
RequestItem item;
if (rpc.has("params")) {
- item = (RequestItem)requestItemConstructor.newInstance(
- (JSONObject)rpc.get("params"), token, converter,
beanJsonConverter);
+ item = (RequestItem) requestItemConstructor.newInstance(
+ (JSONObject) rpc.get("params"), token, converter,
beanJsonConverter);
} else {
- item = (RequestItem)requestItemConstructor.newInstance(new
JSONObject(), token,
+ item = (RequestItem) requestItemConstructor.newInstance(new
JSONObject(), token,
converter, beanJsonConverter);
}
Object result = receiver.invoke(handlerInstance, item);
if (result instanceof Future) {
- return (Future)result;
+ return (Future) result;
}
return ImmediateFuture.newInstance(result);
} catch (InvocationTargetException ite) {
@@ -248,10 +279,28 @@
}
/**
+ * Encapsulate the dispatch of a single RPC
+ */
+ static final class RpcInvocationWrapper implements RpcHandler {
+
+ final RpcInvocationHandler handler;
+ final JSONObject rpc;
+
+ RpcInvocationWrapper(RpcInvocationHandler handler, JSONObject rpc) {
+ this.handler = handler;
+ this.rpc = rpc;
+ }
+
+ public Future<?> execute(SecurityToken st, BeanConverter converter) {
+ return handler.execute(rpc, st, converter);
+ }
+ }
+
+ /**
* Proxy binding for a REST operation. We allow binding to methods that
* return non-Future types by wrapping them in ImmediateFuture.
*/
- static final class RestInvocationHandler implements RestHandler {
+ static final class RestInvocationHandler {
Method receiver;
Object handlerInstance;
Service service;
@@ -267,25 +316,20 @@
this.service = service;
this.operation = operation;
this.receiver = receiver;
- this.handlerInstance = handlerInstance;
+ this.handlerInstance = handlerInstance;
expectedUrl = service.path().split("/");
this.beanJsonConverter = beanJsonConverter;
this.requestItemConstructor = requestItemConstructor;
}
- public Future<?> execute(String path, Map<String, String[]> parameters,
Reader body,
- SecurityToken token, BeanConverter converter) {
- // Create a mutable copy.
- parameters = new HashMap(parameters);
+ public Future<?> execute(Map<String, String[]> parameters, Reader body,
+ SecurityToken token, BeanConverter converter) {
try {
// bind the body contents if available
if (body != null) {
parameters.put(operation.bodyParam(), new
String[]{IOUtils.toString(body)});
}
-
- extractPathParameters(parameters, path);
-
- RequestItem item =
(RequestItem)requestItemConstructor.newInstance(parameters, token,
+ RequestItem item = (RequestItem)
requestItemConstructor.newInstance(parameters, token,
converter, beanJsonConverter);
Object result = receiver.invoke(handlerInstance, item);
if (result instanceof Future) {
@@ -299,30 +343,25 @@
return ImmediateFuture.errorInstance(e);
}
}
+ }
- private void extractPathParameters(Map<String, String[]> parameters,
String path) {
- String[] actualUrl = path.split("/");
+ /**
+ * Encapsulate the dispatch of a single REST call.
+ * Augment the executed parameters with those extracted from the path
+ */
+ static class RestInvocationWrapper implements RestHandler {
+ RestInvocationHandler handler;
+ Map<String, String[]> pathParams;
+
+ RestInvocationWrapper(Map<String, String[]> pathParams,
RestInvocationHandler handler) {
+ this.pathParams = pathParams;
+ this.handler = handler;
+ }
- for (int i = 1; i < actualUrl.length; i++) {
- String actualPart = actualUrl[i];
- String expectedPart = expectedUrl[i - 1];
- // Extract named parameters from the path
- if (expectedPart.startsWith("{")) {
- if (expectedPart.endsWith("}+")) {
- // The param can be a repeated field. Use ',' as default separator
- parameters.put(expectedPart.substring(1, expectedPart.length() -
2),
- actualPart.split(","));
- } else {
- if (actualPart.indexOf(',') != -1) {
- throw new ProtocolException(ResponseError.BAD_REQUEST,
- "Cannot expect plural value " + actualPart
- + " for singular field " + expectedPart + " in " +
service.path());
- }
- parameters.put(expectedPart.substring(1, expectedPart.length() -
1),
- new String[]{actualPart});
- }
- }
- }
+ public Future<?> execute(Map<String, String[]> parameters, Reader body,
+ SecurityToken token, BeanConverter converter) {
+ pathParams.putAll(parameters);
+ return handler.execute(pathParams, body, token, converter);
}
}
@@ -338,7 +377,7 @@
this.error = error;
}
- public Future execute(String path, Map parameters, Reader body,
+ public Future execute(Map parameters, Reader body,
SecurityToken token, BeanConverter converter) {
return ImmediateFuture.errorInstance(error);
}
@@ -355,8 +394,119 @@
this.error = error;
}
- public Future execute(JSONObject rpc, SecurityToken token, BeanConverter
converter) {
+ public Future execute(SecurityToken token, BeanConverter converter) {
return ImmediateFuture.errorInstance(error);
}
}
+
+ /**
+ * Path matching and parameter extraction for REST.
+ */
+ static class RestPath implements Comparable {
+
+ enum PartType {
+ CONST, SINGULAR_PARAM, PLURAL_PARAM
+ }
+
+ class Part {
+ String partName;
+ PartType type;
+ Part(String partName, PartType type) {
+ this.partName = partName;
+ this.type = type;
+ }
+ }
+
+ final String operationPath;
+ final List<Part> parts;
+ final RestInvocationHandler handler;
+ final int constCount;
+ final int lastConstIndex;
+
+ public RestPath(String path, RestInvocationHandler handler) {
+ int tmpConstCount = 0;
+ int tmpConstIndex = -1;
+ this.operationPath = path;
+ String[] partArr = path.substring(1).split("/");
+ parts = Lists.newArrayList();
+ for (int i = 0; i < partArr.length; i++) {
+ String part = partArr[i];
+ PartType type = PartType.CONST;
+ if (part.startsWith("{")) {
+ if (part.endsWith("}+")) {
+ parts.add(new Part(part.substring(1, part.length() - 2),
PartType.PLURAL_PARAM));
+ } else if (part.endsWith("}")) {
+ parts.add(new Part(part.substring(1, part.length() - 1),
PartType.SINGULAR_PARAM));
+ } else {
+ throw new IllegalStateException("Invalid REST path part format " +
part);
+ }
+ } else {
+ parts.add(new Part(part, PartType.CONST));
+ tmpConstCount++;
+ tmpConstIndex = i;
+ }
+ }
+ constCount = tmpConstCount;
+ lastConstIndex = tmpConstIndex;
+ this.handler = handler;
+ }
+
+ /**
+ * See if this Rest path is a match for the requested path
+ * Requested path is offset by 1 as it includes service name
+ * @return A handler with the path parameters decoded, null if not a match
for the path
+ */
+ public RestInvocationWrapper accept(String[] requestPathParts) {
+ if (constCount > 0) {
+ if (lastConstIndex >= requestPathParts.length) {
+ // Last required constant match is not possible with
+ // this request
+ return null;
+ }
+ for (int i = 0; i <= lastConstIndex; i++) {
+ if (parts.get(i).type == PartType.CONST &&
+ !parts.get(i).partName.equals(requestPathParts[i])) {
+ // Constant part does not match request
+ return null;
+ }
+ }
+ }
+
+ // All constant parts matched, extract the parameters
+ Map<String, String[]> parsedParams = Maps.newHashMap();
+ for (int i = 0; i < Math.min(requestPathParts.length, parts.size());
i++) {
+ if (parts.get(i).type == PartType.SINGULAR_PARAM) {
+ if (requestPathParts[i].indexOf(',') != -1) {
+ throw new ProtocolException(ResponseError.BAD_REQUEST,
+ "Cannot expect plural value " + requestPathParts[i]
+ + " for singular field " + parts.get(i) + " for path " +
operationPath);
+ }
+ parsedParams.put(parts.get(i).partName, new
String[]{requestPathParts[i]});
+ } else if (parts.get(i).type == PartType.PLURAL_PARAM) {
+ parsedParams.put(parts.get(i).partName,
requestPathParts[i].split(","));
+ }
+ }
+ return new RestInvocationWrapper(parsedParams, handler);
+ }
+
+ /**
+ * Rank based on the number of consant parts they accept, where the
constant parts occur
+ * and lexical ordering.
+ */
+ public int compareTo(Object o) {
+ RestPath other = (RestPath) o;
+ // Rank first by number of constant elements in the path
+ int result = other.constCount - this.constCount;
+ if (result == 0) {
+ // Rank second by the position of the last constant element
+ // (lower index is better)
+ result = this.lastConstIndex - other.lastConstIndex;
+ }
+ if (result == 0) {
+ // Rank lastly by lexical order
+ result = this.operationPath.compareTo(other.operationPath);
+ }
+ return result;
+ }
+ }
}
Modified:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/JsonRpcServlet.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/JsonRpcServlet.java?rev=743183&r1=743182&r2=743183&view=diff
==============================================================================
---
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/JsonRpcServlet.java
(original)
+++
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/JsonRpcServlet.java
Wed Feb 11 00:44:59 2009
@@ -23,17 +23,19 @@
import org.apache.shindig.common.util.JsonConversionUtil;
import com.google.common.collect.Lists;
+
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.Future;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
/**
* JSON-RPC handler servlet.
*/
@@ -98,7 +100,7 @@
// into single requests.
for (int i = 0; i < batch.length(); i++) {
JSONObject batchObj = batch.getJSONObject(i);
- responses.add(dispatcher.getRpcHandler(batchObj).execute(batchObj,
token, jsonConverter));
+ responses.add(dispatcher.getRpcHandler(batchObj).execute(token,
jsonConverter));
}
// Resolve each Future into a response.
@@ -123,7 +125,7 @@
}
// getRpcHandler never returns null
- Future future = dispatcher.getRpcHandler(request).execute(request, token,
jsonConverter);
+ Future future = dispatcher.getRpcHandler(request).execute(token,
jsonConverter);
// Resolve each Future into a response.
// TODO: should use shared deadline across each request
Modified:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/Operation.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/Operation.java?rev=743183&r1=743182&r2=743183&view=diff
==============================================================================
---
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/Operation.java
(original)
+++
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/Operation.java
Wed Feb 11 00:44:59 2009
@@ -48,4 +48,10 @@
* operation rather than the HTTP method in REST
*/
String path() default "";
+
+ /**
+ * The name to match for the RPC operation to override the default behvaior
+ * which is to use the name of the annotated method
+ */
+ String name() default "";
}
Modified:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/RestHandler.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/RestHandler.java?rev=743183&r1=743182&r2=743183&view=diff
==============================================================================
---
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/RestHandler.java
(original)
+++
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/RestHandler.java
Wed Feb 11 00:44:59 2009
@@ -34,6 +34,6 @@
* Handle the request and return a Future from which the response object
* can be retrieved
*/
- Future<?> execute(String path, Map<String, String[]> parameters, Reader body,
+ Future<?> execute(Map<String, String[]> parameters, Reader body,
SecurityToken token, BeanConverter converter);
}
Modified:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/RpcHandler.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/RpcHandler.java?rev=743183&r1=743182&r2=743183&view=diff
==============================================================================
---
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/RpcHandler.java
(original)
+++
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/RpcHandler.java
Wed Feb 11 00:44:59 2009
@@ -21,8 +21,6 @@
import org.apache.shindig.auth.SecurityToken;
import org.apache.shindig.protocol.conversion.BeanConverter;
-import org.json.JSONObject;
-
import java.util.concurrent.Future;
/**
@@ -34,5 +32,5 @@
* Handle the request and return a Future from which the response object
* can be retrieved
*/
- Future<?> execute(JSONObject rpc, SecurityToken st, BeanConverter converter);
+ Future<?> execute(SecurityToken st, BeanConverter converter);
}
Modified:
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/DefaultHandlerRegistryTest.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/DefaultHandlerRegistryTest.java?rev=743183&r1=743182&r2=743183&view=diff
==============================================================================
---
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/DefaultHandlerRegistryTest.java
(original)
+++
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/DefaultHandlerRegistryTest.java
Wed Feb 11 00:44:59 2009
@@ -22,10 +22,15 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
+
import junit.framework.Assert;
import junit.framework.TestCase;
+
import org.json.JSONObject;
+import static org.junit.Assert.assertArrayEquals;
+import java.util.Iterator;
+import java.util.TreeSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@@ -56,6 +61,10 @@
assertNotNull(registry.getRpcHandler(new JSONObject("{method :
test.overidden}")));
}
+ public void testOverrideHandlerRPCName() throws Exception {
+ assertNotNull(registry.getRpcHandler(new JSONObject("{method :
test.override.rpcname}")));
+ }
+
public void testOverrideHandlerRest() throws Exception {
assertNotNull(registry.getRestHandler("/test/overidden/method/", "GET"));
}
@@ -68,7 +77,7 @@
JSONObject rpc = new JSONObject("{method : makebelieve.get}");
RpcHandler rpcHandler = registry.getRpcHandler(rpc);
try {
- Future future = rpcHandler.execute(rpc, null, null);
+ Future future = rpcHandler.execute(null, null);
future.get();
fail("Expect exception for missing method");
} catch (ExecutionException t) {
@@ -82,8 +91,7 @@
public void testRestHandler_serviceDoesntExist() {
RestHandler restHandler = registry.getRestHandler("/makebelieve", "GET");
try {
- Future future = restHandler.execute("/makebelieve", Maps.<String,
String[]>newHashMap(),
- null, null, null);
+ Future future = restHandler.execute(Maps.<String, String[]>newHashMap(),
null, null, null);
future.get();
fail("Expect exception for missing method");
} catch (ExecutionException t) {
@@ -97,14 +105,14 @@
public void testNonFutureDispatch() throws Exception {
// Test calling a handler method which does not return a future
RestHandler handler = registry.getRestHandler("/test", "GET");
- Future future = handler.execute("/test", Maps.<String,
String[]>newHashMap(), null, null, null);
+ Future future = handler.execute(Maps.<String, String[]>newHashMap(), null,
null, null);
assertEquals(future.get(), TestHandler.GET_RESPONSE);
}
public void testFutureDispatch() throws Exception {
// Test calling a handler method which does not return a future
RestHandler handler = registry.getRestHandler("/test", "POST");
- Future future = handler.execute("/test", Maps.<String,
String[]>newHashMap(), null, null, null);
+ Future future = handler.execute(Maps.<String, String[]>newHashMap(), null,
null, null);
assertEquals(future.get(), TestHandler.CREATE_RESPONSE);
}
@@ -112,7 +120,7 @@
// Test calling a handler method which does not return a future
JSONObject rpc = new JSONObject("{ method : test.exception }");
RpcHandler handler = registry.getRpcHandler(rpc);
- Future future = handler.execute(rpc, null, null);
+ Future future = handler.execute(null, null);
try {
future.get();
fail("Service method did not produce NullPointerException from Future");
@@ -125,7 +133,7 @@
// Test calling a handler method which does not return a future
JSONObject rpc = new JSONObject("{ method : test.futureException }");
RpcHandler handler = registry.getRpcHandler(rpc);
- Future future = handler.execute(rpc, null, null);
+ Future future = handler.execute(null, null);
try {
future.get();
fail("Service method did not produce ExecutionException from Future");
@@ -137,12 +145,15 @@
public void testSupportedRpcServices() throws Exception {
assertEquals(registry.getSupportedRpcServices(),
Sets.newHashSet("test.create", "test.get", "test.overridden",
"test.exception",
- "test.futureException"));
+ "test.futureException", "test.override.rpcname"));
}
public void testSupportedRestServices() throws Exception {
assertEquals(registry.getSupportedRestServices(),
- Sets.newHashSet("GET /test", "PUT /test", "DELETE /test", "POST /test",
+ Sets.newHashSet("GET /test/{someParam}/{someOtherParam}",
+ "PUT /test/{someParam}/{someOtherParam}",
+ "DELETE /test/{someParam}/{someOtherParam}",
+ "POST /test/{someParam}/{someOtherParam}",
"GET /test/overridden/method"));
}
@@ -154,4 +165,35 @@
}
}
+
+ public void testRestPath() {
+ DefaultHandlerRegistry.RestPath restPath =
+ new
DefaultHandlerRegistry.RestPath("/service/const1/{p1}/{p2}+/const2/{p3}", null);
+ DefaultHandlerRegistry.RestInvocationWrapper wrapper =
+ restPath.accept("service/const1/a/b,c/const2/d".split("/"));
+ assertArrayEquals(wrapper.pathParams.get("p1"), new String[]{"a"});
+ assertArrayEquals(wrapper.pathParams.get("p2"), new String[]{"b","c"});
+ assertArrayEquals(wrapper.pathParams.get("p3"), new String[]{"d"});
+ wrapper = restPath.accept("service/const1/a/b/const2".split("/"));
+ assertArrayEquals(wrapper.pathParams.get("p1"), new String[]{"a"});
+ assertArrayEquals(wrapper.pathParams.get("p2"), new String[]{"b"});
+ assertNull(wrapper.pathParams.get("p3"));
+ assertNull(restPath.accept("service/const1/{p1}/{p2}+".split("/")));
+
assertNull(restPath.accept("service/constmiss/{p1}/{p2}+/const2".split("/")));
+ }
+
+ public void testRestPathOrdering() {
+ DefaultHandlerRegistry.RestPath restPath1 =
+ new
DefaultHandlerRegistry.RestPath("/service/const1/{p1}/{p2}+/const2/{p3}", null);
+ DefaultHandlerRegistry.RestPath restPath2 =
+ new DefaultHandlerRegistry.RestPath("/service/{p1}/{p2}+/const2/{p3}",
null);
+ DefaultHandlerRegistry.RestPath restPath3 =
+ new
DefaultHandlerRegistry.RestPath("/service/const1/const2/{p1}/{p2}+/{p3}", null);
+ TreeSet<DefaultHandlerRegistry.RestPath> sortedSet =
+ Sets.newTreeSet(restPath1, restPath2, restPath3);
+ Iterator<DefaultHandlerRegistry.RestPath> itr = sortedSet.iterator();
+ assertEquals(itr.next(), restPath3);
+ assertEquals(itr.next(), restPath1);
+ assertEquals(itr.next(), restPath2);
+ }
}
Modified:
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/TestHandler.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/TestHandler.java?rev=743183&r1=743182&r2=743183&view=diff
==============================================================================
---
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/TestHandler.java
(original)
+++
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/TestHandler.java
Wed Feb 11 00:44:59 2009
@@ -21,6 +21,7 @@
import org.apache.shindig.common.util.ImmediateFuture;
import com.google.common.collect.ImmutableMap;
+
import org.junit.Ignore;
import java.util.Map;
@@ -66,6 +67,14 @@
return GET_RESPONSE;
}
+ @Operation(name="override.rpcname", httpMethods = "")
+ public String overriddenRpc(RequestItem req) {
+ if (mock != null) {
+ return mock.get(req);
+ }
+ return GET_RESPONSE;
+ }
+
@Operation(httpMethods = {"POST", "PUT"})
public Future<?> create(RequestItem req) {
if (mock != null) {
Modified:
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/ActivityHandlerTest.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/ActivityHandlerTest.java?rev=743183&r1=743182&r2=743183&view=diff
==============================================================================
---
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/ActivityHandlerTest.java
(original)
+++
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/ActivityHandlerTest.java
Wed Feb 11 00:44:59 2009
@@ -40,6 +40,7 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
+
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.isNull;
import org.json.JSONObject;
@@ -96,7 +97,7 @@
andReturn(ImmediateFuture.newInstance(data));
replay();
- assertEquals(data, operation.execute(path, Maps.<String,
String[]>newHashMap(),
+ assertEquals(data, operation.execute(Maps.<String, String[]>newHashMap(),
null, token, converter).get());
verify();
reset();
@@ -128,7 +129,7 @@
ImmediateFuture.newInstance(data));
replay();
- assertEquals(data, operation.execute(path, Maps.<String,
String[]>newHashMap(),
+ assertEquals(data, operation.execute(Maps.<String, String[]>newHashMap(),
null, token, converter).get());
verify();
reset();
@@ -145,7 +146,7 @@
ImmediateFuture.newInstance(activity));
replay();
- assertEquals(activity, operation.execute(path, Maps.<String,
String[]>newHashMap(),
+ assertEquals(activity, operation.execute(Maps.<String,
String[]>newHashMap(),
null, token, converter).get());
verify();
reset();
@@ -166,7 +167,7 @@
eq(activity), eq(token))).andReturn(ImmediateFuture.newInstance((Void)
null));
replay();
- return operation.execute(path, Maps.<String, String[]>newHashMap(),
+ return operation.execute(Maps.<String, String[]>newHashMap(),
new StringReader(jsonActivity), token, converter);
}
@@ -194,7 +195,7 @@
eq(token))).andReturn(ImmediateFuture.newInstance((Void) null));
replay();
- assertNull(operation.execute(path, Maps.<String, String[]>newHashMap(),
null,
+ assertNull(operation.execute(Maps.<String, String[]>newHashMap(), null,
token, converter).get());
verify();
reset();
@@ -206,8 +207,8 @@
replay();
@SuppressWarnings("unchecked")
- List<Object> received = (List<Object>) operation.execute(path,
Maps.<String, String[]>newHashMap(), null,
- token, converter).get();
+ List<Object> received = (List<Object>) operation.execute(Maps.<String,
String[]>newHashMap(),
+ null, token, converter).get();
assertEquals(2, received.size());
assertEquals("id", received.get(0).toString());
assertEquals("title", received.get(1).toString());
Modified:
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/AppDataHandlerTest.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/AppDataHandlerTest.java?rev=743183&r1=743182&r2=743183&view=diff
==============================================================================
---
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/AppDataHandlerTest.java
(original)
+++
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/AppDataHandlerTest.java
Wed Feb 11 00:44:59 2009
@@ -34,6 +34,7 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
+
import org.easymock.classextension.EasyMock;
import static org.easymock.classextension.EasyMock.eq;
@@ -81,7 +82,7 @@
.andReturn(ImmediateFuture.newInstance(data));
replay();
- assertEquals(data, operation.execute(path, Maps.<String,
String[]>newHashMap(),
+ assertEquals(data, operation.execute(Maps.<String, String[]>newHashMap(),
null, token, converter).get());
verify();
}
@@ -111,7 +112,7 @@
.andReturn(ImmediateFuture.newInstance(data));
replay();
- assertEquals(data, operation.execute(path, Maps.<String,
String[]>newHashMap(),
+ assertEquals(data, operation.execute(Maps.<String, String[]>newHashMap(),
null, token, converter).get());
verify();
}
@@ -130,7 +131,7 @@
.andReturn(ImmediateFuture.newInstance(data));
replay();
- assertEquals(data, operation.execute(path, params, null, token,
converter).get());
+ assertEquals(data, operation.execute(params, null, token,
converter).get());
verify();
}
@@ -152,7 +153,7 @@
eq("appId"), eq(Sets.newHashSet("pandas")), eq(values), eq(token)))
.andReturn(ImmediateFuture.newInstance((Void) null));
replay();
- return operation.execute(path, params, new StringReader(jsonAppData),
token, converter);
+ return operation.execute(params, new StringReader(jsonAppData), token,
converter);
}
public void testHandlePost() throws Exception {
@@ -186,7 +187,7 @@
replay();
try {
- operation.execute(path, params, new StringReader(jsonAppData), token,
converter).get();
+ operation.execute(params, new StringReader(jsonAppData), token,
converter).get();
fail();
} catch (ExecutionException ee) {
assertEquals(((SocialSpiException)ee.getCause()).getError(),
ResponseError.BAD_REQUEST);
@@ -215,7 +216,7 @@
replay();
try {
- operation.execute(path, params, new StringReader(jsonAppData), token,
converter).get();
+ operation.execute(params, new StringReader(jsonAppData), token,
converter).get();
fail();
} catch (ExecutionException ee) {
assertEquals(((SocialSpiException)ee.getCause()).getError(),
ResponseError.BAD_REQUEST);
@@ -236,7 +237,7 @@
.andReturn(ImmediateFuture.newInstance((Void) null));
replay();
- assertNull(operation.execute(path, params, null, token, converter).get());
+ assertNull(operation.execute(params, null, token, converter).get());
verify();
}
}
Modified:
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/PersonHandlerTest.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/PersonHandlerTest.java?rev=743183&r1=743182&r2=743183&view=diff
==============================================================================
---
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/PersonHandlerTest.java
(original)
+++
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/PersonHandlerTest.java
Wed Feb 11 00:44:59 2009
@@ -42,6 +42,7 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
+
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import org.json.JSONObject;
@@ -108,7 +109,7 @@
.andReturn(ImmediateFuture.newInstance(data));
replay();
- assertEquals(data, operation.execute(path, Maps.<String,
String[]>newHashMap(), null,
+ assertEquals(data, operation.execute(Maps.<String, String[]>newHashMap(),
null,
token, converter).get());
verify();
}
@@ -128,7 +129,7 @@
.andReturn(ImmediateFuture.newInstance(data));
replay();
- assertEquals(data, operation.execute(path, Maps.<String,
String[]>newHashMap(),
+ assertEquals(data, operation.execute(Maps.<String, String[]>newHashMap(),
null, token, converter).get());
verify();
}
@@ -166,7 +167,7 @@
.andReturn(ImmediateFuture.newInstance(data));
replay();
- assertEquals(data, operation.execute(path, params, null, token,
converter).get());
+ assertEquals(data, operation.execute(params, null, token,
converter).get());
verify();
}
@@ -180,7 +181,7 @@
eq(DEFAULT_FIELDS),
eq(token))).andReturn(ImmediateFuture.newInstance(data));
replay();
- assertEquals(data, operation.execute(path, Maps.<String,
String[]>newHashMap(),
+ assertEquals(data, operation.execute(Maps.<String, String[]>newHashMap(),
null, token, converter).get());
verify();
}
@@ -194,7 +195,7 @@
eq(DEFAULT_FIELDS),
eq(token))).andReturn(ImmediateFuture.newInstance(data));
replay();
- assertEquals(data, operation.execute(path, Maps.<String,
String[]>newHashMap(),
+ assertEquals(data, operation.execute(Maps.<String, String[]>newHashMap(),
null, token, converter).get());
verify();
}
@@ -214,7 +215,7 @@
eq(token))).andReturn(ImmediateFuture.newInstance(data));
replay();
- assertEquals(data, operation.execute(path, Maps.<String,
String[]>newHashMap(),
+ assertEquals(data, operation.execute(Maps.<String, String[]>newHashMap(),
null, token, converter).get());
verify();
}
@@ -225,8 +226,8 @@
replay();
@SuppressWarnings("unchecked")
- List<Object> received = (List<Object>) operation.execute(path,
Maps.<String, String[]>newHashMap(), null,
- token, converter).get();
+ List<Object> received = (List<Object>) operation.execute(Maps.<String,
String[]>newHashMap(),
+ null, token, converter).get();
assertEquals(2, received.size());
assertEquals("id", received.get(0).toString());
@SuppressWarnings("unchecked")