You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by lr...@apache.org on 2009/02/02 19:39:16 UTC
svn commit: r740067 [1/2] - in /incubator/shindig/trunk/java:
server/src/test/java/org/apache/shindig/server/endtoend/
social-api/src/main/java/org/apache/shindig/social/core/config/
social-api/src/main/java/org/apache/shindig/social/opensocial/service...
Author: lryan
Date: Mon Feb 2 18:39:12 2009
New Revision: 740067
URL: http://svn.apache.org/viewvc?rev=740067&view=rev
Log:
Annotation based handler binding and dispatch
Added:
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/BaseRequestItem.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/DefaultHandlerRegistry.java (contents, props changed)
- copied, changed from r738302, incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/StandardHandlerDispatcher.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/HandlerPreconditions.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/HandlerRegistry.java (contents, props changed)
- copied, changed from r738302, incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/HandlerDispatcher.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/Operation.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/RestHandler.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/RpcHandler.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/Service.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/SystemHandler.java
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/BaseRequestItemTest.java
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/DefaultHandlerRegistryTest.java
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/TestHandler.java
Removed:
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/DataRequestHandler.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/HandlerDispatcher.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/RestfulRequestItem.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/RpcRequestItem.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/StandardHandlerDispatcher.java
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/RestfulRequestItemTest.java
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/RpcRequestItemTest.java
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/StandardHandlerDispatcherTest.java
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/spi/DataRequestHandlerTest.java
Modified:
incubator/shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndModule.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/config/SocialApiGuiceModule.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/ActivityHandler.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/ApiServlet.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/AppDataHandler.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/DataServiceServlet.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/JsonRpcServlet.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/PersonHandler.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/RequestItem.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/service/SampleContainerHandler.java
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/SocialApiTestsGuiceModule.java
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/dataservice/integration/AbstractLargeRestfulTests.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/DataServiceServletTest.java
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/JsonRpcServletTest.java
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/PersonHandlerTest.java
Modified: incubator/shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndModule.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndModule.java?rev=740067&r1=740066&r2=740067&view=diff
==============================================================================
--- incubator/shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndModule.java (original)
+++ incubator/shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndModule.java Mon Feb 2 18:39:12 2009
@@ -20,13 +20,19 @@
import org.apache.shindig.auth.AnonymousAuthenticationHandler;
import org.apache.shindig.auth.AuthenticationHandler;
import org.apache.shindig.common.servlet.ParameterFetcher;
+import org.apache.shindig.config.ContainerConfig;
+import org.apache.shindig.config.JsonContainerConfig;
import org.apache.shindig.social.core.oauth.AuthenticationHandlerProvider;
import org.apache.shindig.social.core.util.BeanJsonConverter;
import org.apache.shindig.social.core.util.BeanXStreamAtomConverter;
import org.apache.shindig.social.core.util.BeanXStreamConverter;
+import org.apache.shindig.social.opensocial.service.ActivityHandler;
+import org.apache.shindig.social.opensocial.service.AppDataHandler;
import org.apache.shindig.social.opensocial.service.BeanConverter;
import org.apache.shindig.social.opensocial.service.DataServiceServletFetcher;
+import org.apache.shindig.social.opensocial.service.PersonHandler;
+import com.google.common.collect.Lists;
import com.google.inject.AbstractModule;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
@@ -58,5 +64,11 @@
bind(new TypeLiteral<List<AuthenticationHandler>>(){}).toProvider(
AuthenticationHandlerProvider.class);
+
+ bind(List.class).annotatedWith(Names.named("org.apache.shindig.handlers"))
+ .toInstance(Lists.immutableList(ActivityHandler.class, AppDataHandler.class,
+ PersonHandler.class));
+
+ bind(ContainerConfig.class).to(JsonContainerConfig.class);
}
}
Modified: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/config/SocialApiGuiceModule.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/config/SocialApiGuiceModule.java?rev=740067&r1=740066&r2=740067&view=diff
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/config/SocialApiGuiceModule.java (original)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/config/SocialApiGuiceModule.java Mon Feb 2 18:39:12 2009
@@ -25,15 +25,17 @@
import org.apache.shindig.social.core.util.BeanJsonConverter;
import org.apache.shindig.social.core.util.BeanXStreamAtomConverter;
import org.apache.shindig.social.core.util.BeanXStreamConverter;
+import org.apache.shindig.social.opensocial.service.ActivityHandler;
+import org.apache.shindig.social.opensocial.service.AppDataHandler;
import org.apache.shindig.social.opensocial.service.BeanConverter;
import org.apache.shindig.social.opensocial.service.DataServiceServletFetcher;
-import org.apache.shindig.social.opensocial.service.StandardHandlerDispatcher;
-import org.apache.shindig.social.opensocial.service.HandlerDispatcher;
+import org.apache.shindig.social.opensocial.service.DefaultHandlerRegistry;
+import org.apache.shindig.social.opensocial.service.HandlerRegistry;
+import org.apache.shindig.social.opensocial.service.PersonHandler;
import org.apache.shindig.social.sample.service.SampleContainerHandler;
+import com.google.common.collect.Lists;
import com.google.inject.AbstractModule;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
@@ -50,7 +52,7 @@
/** {@inheritDoc} */
@Override
protected void configure() {
- bind(HandlerDispatcher.class).toProvider(HandlerDispatcherProvider.class);
+ bind(HandlerRegistry.class).to(DefaultHandlerRegistry.class);
bind(ParameterFetcher.class).annotatedWith(Names.named("DataServiceServlet"))
.to(DataServiceServletFetcher.class);
@@ -71,24 +73,9 @@
bind(new TypeLiteral<List<AuthenticationHandler>>(){}).toProvider(
AuthenticationHandlerProvider.class);
- }
-
- /**
- * Provider for the HandlerDispatcher. Adds the sample container handler
- * at "samplecontainer".
- */
- static class HandlerDispatcherProvider implements Provider<HandlerDispatcher> {
- private final HandlerDispatcher dispatcher;
-
- @Inject
- public HandlerDispatcherProvider(StandardHandlerDispatcher dispatcher,
- Provider<SampleContainerHandler> sampleHandler) {
- dispatcher.addHandler("samplecontainer", sampleHandler);
- this.dispatcher = dispatcher;
- }
-
- public HandlerDispatcher get() {
- return dispatcher;
- }
+
+ bind(List.class).annotatedWith(Names.named("org.apache.shindig.handlers"))
+ .toInstance(Lists.immutableList(ActivityHandler.class, AppDataHandler.class,
+ PersonHandler.class, SampleContainerHandler.class));
}
}
Modified: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/ActivityHandler.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/ActivityHandler.java?rev=740067&r1=740066&r2=740067&view=diff
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/ActivityHandler.java (original)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/ActivityHandler.java Mon Feb 2 18:39:12 2009
@@ -17,30 +17,35 @@
*/
package org.apache.shindig.social.opensocial.service;
+import org.apache.shindig.config.ContainerConfig;
import org.apache.shindig.social.opensocial.model.Activity;
import org.apache.shindig.social.opensocial.spi.ActivityService;
+import org.apache.shindig.social.opensocial.spi.CollectionOptions;
import org.apache.shindig.social.opensocial.spi.SocialSpiException;
import org.apache.shindig.social.opensocial.spi.UserId;
-import org.apache.shindig.social.opensocial.spi.CollectionOptions;
-import com.google.common.collect.Sets;
-import com.google.common.collect.Iterables;
+import com.google.common.base.Objects;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
import com.google.inject.Inject;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
+import java.util.logging.Logger;
-public class ActivityHandler extends DataRequestHandler {
- private final ActivityService service;
+@Service(name = "activities", path="/{userId}+/{groupId}/{appId}/{activityId}+")
+public class ActivityHandler {
- private static final String ACTIVITY_ID_PATH
- = "/activities/{userId}+/{groupId}/{appId}/{activityId}+";
+ private static final Logger logger = Logger.getLogger(ActivityHandler.class.getName());
+
+ private final ActivityService service;
+ private final ContainerConfig config;
@Inject
- public ActivityHandler(ActivityService service) {
+ public ActivityHandler(ActivityService service, ContainerConfig config) {
this.service = service;
+ this.config = config;
}
/**
@@ -48,16 +53,15 @@
*
* examples: /activities/john.doe/@self/1
*/
- @Override
- protected Future<?> handleDelete(RequestItem request)
+ @Operation(httpMethods="DELETE")
+ public Future<?> delete(RequestItem request)
throws SocialSpiException {
- request.applyUrlTemplate(ACTIVITY_ID_PATH);
Set<UserId> userIds = request.getUsers();
Set<String> activityIds = ImmutableSet.copyOf(request.getListParameter("activityId"));
- Preconditions.requireNotEmpty(userIds, "No userId specified");
- Preconditions.requireSingular(userIds, "Multiple userIds not supported");
+ HandlerPreconditions.requireNotEmpty(userIds, "No userId specified");
+ HandlerPreconditions.requireSingular(userIds, "Multiple userIds not supported");
// Throws exceptions if userIds contains more than one element or zero elements
return service.deleteActivities(Iterables.getOnlyElement(userIds), request.getGroup(),
request.getAppId(), activityIds, request.getToken());
@@ -68,9 +72,9 @@
*
* examples: /activities/john.doe/@self - postBody is an activity object
*/
- @Override
- protected Future<?> handlePut(RequestItem request) throws SocialSpiException {
- return handlePost(request);
+ @Operation(httpMethods="PUT", bodyParam = "activity")
+ public Future<?> update(RequestItem request) throws SocialSpiException {
+ return create(request);
}
/**
@@ -78,17 +82,16 @@
*
* examples: /activities/john.doe/@self - postBody is an activity object
*/
- @Override
- protected Future<?> handlePost(RequestItem request) throws SocialSpiException {
- request.applyUrlTemplate(ACTIVITY_ID_PATH);
+ @Operation(httpMethods="POST", bodyParam = "activity")
+ public Future<?> create(RequestItem request) throws SocialSpiException {
Set<UserId> userIds = request.getUsers();
List<String> activityIds = request.getListParameter("activityId");
- Preconditions.requireNotEmpty(userIds, "No userId specified");
- Preconditions.requireSingular(userIds, "Multiple userIds not supported");
+ HandlerPreconditions.requireNotEmpty(userIds, "No userId specified");
+ HandlerPreconditions.requireSingular(userIds, "Multiple userIds not supported");
// TODO(lryan) This seems reasonable to allow on PUT but we don't have an update verb.
- Preconditions.requireEmpty(activityIds, "Cannot specify activityId in create");
+ HandlerPreconditions.requireEmpty(activityIds, "Cannot specify activityId in create");
return service.createActivity(Iterables.getOnlyElement(userIds), request.getGroup(),
request.getAppId(), request.getFields(),
@@ -103,18 +106,16 @@
* examples: /activities/john.doe/@self/1 /activities/john.doe/@self
* /activities/john.doe,jane.doe/@friends
*/
- @Override
- protected Future<?> handleGet(RequestItem request)
+ @Operation(httpMethods="GET")
+ public Future<?> get(RequestItem request)
throws SocialSpiException {
- request.applyUrlTemplate(ACTIVITY_ID_PATH);
-
Set<UserId> userIds = request.getUsers();
Set<String> optionalActivityIds = ImmutableSet.copyOf(request.getListParameter("activityId"));
CollectionOptions options = new CollectionOptions(request);
// Preconditions
- Preconditions.requireNotEmpty(userIds, "No userId specified");
+ HandlerPreconditions.requireNotEmpty(userIds, "No userId specified");
if (userIds.size() > 1 && !optionalActivityIds.isEmpty()) {
throw new IllegalArgumentException("Cannot fetch same activityIds for multiple userIds");
}
@@ -136,4 +137,13 @@
// getSortBy(params), getFilterBy(params), getStartIndex(params), getCount(params),
request.getFields(), options, request.getToken());
}
+
+ @Operation(httpMethods = "GET", path="/@supportedFields")
+ public List supportedFields(RequestItem request) {
+ // TODO: Would be nice if name in config matched name of service.
+ String container = Objects.firstNonNull(request.getToken().getContainer(),
+ ContainerConfig.DEFAULT_CONTAINER);
+ return config.getList(container,
+ "${gadgets\\.features.opensocial-0\\.8.supportedFields.activity}");
+ }
}
Modified: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/ApiServlet.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/ApiServlet.java?rev=740067&r1=740066&r2=740067&view=diff
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/ApiServlet.java (original)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/ApiServlet.java Mon Feb 2 18:39:12 2009
@@ -20,7 +20,6 @@
import org.apache.shindig.auth.AuthInfo;
import org.apache.shindig.auth.SecurityToken;
import org.apache.shindig.common.servlet.InjectedServlet;
-import org.apache.shindig.common.util.ImmediateFuture;
import org.apache.shindig.social.ResponseError;
import org.apache.shindig.social.core.util.BeanJsonConverter;
import org.apache.shindig.social.opensocial.spi.SocialSpiException;
@@ -28,12 +27,12 @@
import com.google.inject.Inject;
import com.google.inject.name.Named;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
/**
* Common base class for API servlets.
@@ -41,13 +40,13 @@
public abstract class ApiServlet extends InjectedServlet {
protected static final String DEFAULT_ENCODING = "UTF-8";
- private HandlerDispatcher dispatcher;
+ protected HandlerRegistry dispatcher;
protected BeanJsonConverter jsonConverter;
protected BeanConverter xmlConverter;
protected BeanConverter atomConverter;
@Inject
- public void setHandlerDispatcher(HandlerDispatcher dispatcher) {
+ public void setHandlerRegistry(HandlerRegistry dispatcher) {
this.dispatcher = dispatcher;
}
@@ -75,21 +74,6 @@
+ "requests are not allowed"));
}
- /**
- * Delivers a request item to the appropriate DataRequestHandler.
- */
- protected Future<?> handleRequestItem(RequestItem requestItem,
- HttpServletRequest servletRequest) {
- DataRequestHandler handler = dispatcher.getHandler(requestItem.getService());
-
- if (handler == null) {
- return ImmediateFuture.errorInstance(new SocialSpiException(ResponseError.NOT_IMPLEMENTED,
- "The service " + requestItem.getService() + " is not implemented"));
- }
-
- return handler.handleItem(requestItem);
- }
-
protected ResponseItem getResponseItem(Future<?> future) {
ResponseItem response;
try {
Modified: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/AppDataHandler.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/AppDataHandler.java?rev=740067&r1=740066&r2=740067&view=diff
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/AppDataHandler.java (original)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/AppDataHandler.java Mon Feb 2 18:39:12 2009
@@ -29,12 +29,11 @@
import java.util.Set;
import java.util.concurrent.Future;
-public class AppDataHandler extends DataRequestHandler {
+@Service(name = "appdata", path = "/{userId}+/{groupId}/{appId}")
+public class AppDataHandler {
private final AppDataService service;
- private static final String APP_DATA_PATH = "/appdata/{userId}+/{groupId}/{appId}";
-
@Inject
public AppDataHandler(AppDataService service) {
this.service = service;
@@ -49,15 +48,14 @@
* values and set on the person object. If there are no fields vars then all of the data will be
* overridden.
*/
- @Override
- protected Future<?> handleDelete(RequestItem request)
+ @Operation(httpMethods = "DELETE")
+ public Future<?> delete(RequestItem request)
throws SocialSpiException {
- request.applyUrlTemplate(APP_DATA_PATH);
Set<UserId> userIds = request.getUsers();
- Preconditions.requireNotEmpty(userIds, "No userId specified");
- Preconditions.requireSingular(userIds, "Multiple userIds not supported");
+ HandlerPreconditions.requireNotEmpty(userIds, "No userId specified");
+ HandlerPreconditions.requireSingular(userIds, "Multiple userIds not supported");
return service.deletePersonData(userIds.iterator().next(), request.getGroup(),
request.getAppId(), request.getFields(), request.getToken());
@@ -72,9 +70,9 @@
* values and set on the person object. If there are no fields vars then all of the data will be
* overridden.
*/
- @Override
- protected Future<?> handlePut(RequestItem request) throws SocialSpiException {
- return handlePost(request);
+ @Operation(httpMethods = "PUT", bodyParam = "data")
+ public Future<?> update(RequestItem request) throws SocialSpiException {
+ return create(request);
}
/**
@@ -85,15 +83,12 @@
* The post data should be a regular json object. All of the fields vars will be pulled from the
* values and set. If there are no fields vars then all of the data will be overridden.
*/
- @SuppressWarnings("unchecked")
- @Override
- protected Future<?> handlePost(RequestItem request) throws SocialSpiException {
- request.applyUrlTemplate(APP_DATA_PATH);
-
+ @Operation(httpMethods = "POST", bodyParam = "data")
+ public Future<?> create(RequestItem request) throws SocialSpiException {
Set<UserId> userIds = request.getUsers();
- Preconditions.requireNotEmpty(userIds, "No userId specified");
- Preconditions.requireSingular(userIds, "Multiple userIds not supported");
+ HandlerPreconditions.requireNotEmpty(userIds, "No userId specified");
+ HandlerPreconditions.requireSingular(userIds, "Multiple userIds not supported");
Map<String, String> values = request.getTypedParameter("data", HashMap.class);
for (String key : values.keySet()) {
@@ -112,14 +107,12 @@
*
* examples: /appdata/john.doe/@friends/app?fields=count /appdata/john.doe/@self/app
*/
- @Override
- protected Future<?> handleGet(RequestItem request) throws SocialSpiException {
- request.applyUrlTemplate(APP_DATA_PATH);
-
+ @Operation(httpMethods = "GET")
+ public Future<?> get(RequestItem request) throws SocialSpiException {
Set<UserId> userIds = request.getUsers();
// Preconditions
- Preconditions.requireNotEmpty(userIds, "No userId specified");
+ HandlerPreconditions.requireNotEmpty(userIds, "No userId specified");
return service.getPersonData(userIds, request.getGroup(),
request.getAppId(), request.getFields(), request.getToken());
Added: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/BaseRequestItem.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/BaseRequestItem.java?rev=740067&view=auto
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/BaseRequestItem.java (added)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/BaseRequestItem.java Mon Feb 2 18:39:12 2009
@@ -0,0 +1,280 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.apache.shindig.social.opensocial.service;
+
+import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.social.ResponseError;
+import org.apache.shindig.social.opensocial.spi.GroupId;
+import org.apache.shindig.social.opensocial.spi.PersonService;
+import org.apache.shindig.social.opensocial.spi.SocialSpiException;
+import org.apache.shindig.social.opensocial.spi.UserId;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.joda.time.DateTime;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Default implementation of RequestItem
+ */
+public class BaseRequestItem implements RequestItem {
+
+ final SecurityToken token;
+ final BeanConverter converter;
+ final Map<String,Object> parameters;
+
+ public BaseRequestItem(Map<String, String[]> parameters,
+ SecurityToken token,
+ BeanConverter converter) {
+ this.token = token;
+ this.converter = converter;
+ this.parameters = Maps.newHashMap();
+ for (Map.Entry<String, String[]> entry : parameters.entrySet()) {
+ if (entry.getValue() == null) {
+ setParameter(entry.getKey(), null);
+ } else if (entry.getValue().length == 1) {
+ setParameter(entry.getKey(), entry.getValue()[0]);
+ } else {
+ setParameter(entry.getKey(), Lists.newArrayList(entry.getValue()));
+ }
+ }
+ }
+
+ public BaseRequestItem(JSONObject parameters,
+ SecurityToken token,
+ BeanConverter converter) {
+ try {
+ this.parameters = Maps.newHashMap();
+ Iterator keys = parameters.keys();
+ while (keys.hasNext()) {
+ String key = (String)keys.next();
+ this.parameters.put(key, parameters.get(key));
+ }
+ this.token = token;
+ this.converter = converter;
+ } catch (JSONException je) {
+ throw new SocialSpiException(ResponseError.INTERNAL_ERROR, je.getMessage(), je);
+ }
+ }
+
+ public String getAppId() {
+ String appId = getParameter(APP_ID);
+ if (appId != null && appId.equals(APP_SUBSTITUTION_TOKEN)) {
+ return token.getAppId();
+ } else {
+ return appId;
+ }
+ }
+
+ public Date getUpdatedSince() {
+ String updatedSince = getParameter("updatedSince");
+ if (updatedSince == null)
+ return null;
+
+ DateTime date = new DateTime(updatedSince);
+
+ return date.toDate();
+ }
+
+ public Set<UserId> getUsers() {
+ List<String> ids = getListParameter(USER_ID);
+ if (ids.isEmpty()) {
+ if (token.getViewerId() != null) {
+ // Assume @me
+ ids = Lists.newArrayList("@me");
+ } else {
+ throw new IllegalArgumentException("No userId provided and viewer not available");
+ }
+ }
+ Set<UserId> userIds = Sets.newLinkedHashSet();
+ for (String id : ids) {
+ userIds.add(UserId.fromJson(id));
+ }
+ return userIds;
+ }
+
+
+ public GroupId getGroup() {
+ return GroupId.fromJson(getParameter(GROUP_ID, "@self"));
+ }
+
+ public int getStartIndex() {
+ String startIndex = getParameter(START_INDEX);
+ try {
+ return startIndex == null ? DEFAULT_START_INDEX
+ : Integer.valueOf(startIndex);
+ } catch (NumberFormatException nfe) {
+ throw new SocialSpiException(ResponseError.BAD_REQUEST,
+ "Parameter " + START_INDEX + " (" + startIndex + ") is not a number.");
+ }
+ }
+
+ public int getCount() {
+ String count = getParameter(COUNT);
+ try {
+ return count == null ? DEFAULT_COUNT : Integer.valueOf(count);
+ } catch (NumberFormatException nfe) {
+ throw new SocialSpiException(ResponseError.BAD_REQUEST,
+ "Parameter " + COUNT + " (" + count + ") is not a number.");
+ }
+ }
+
+ public String getSortBy() {
+ String sortBy = getParameter(SORT_BY);
+ return sortBy == null ? PersonService.TOP_FRIENDS_SORT : sortBy;
+ }
+
+ public PersonService.SortOrder getSortOrder() {
+ String sortOrder = getParameter(SORT_ORDER);
+ try {
+ return sortOrder == null
+ ? PersonService.SortOrder.ascending
+ : PersonService.SortOrder.valueOf(sortOrder);
+ } catch (IllegalArgumentException iae) {
+ throw new SocialSpiException(ResponseError.BAD_REQUEST,
+ "Parameter " + SORT_ORDER + " (" + sortOrder + ") is not valid.");
+ }
+ }
+
+ public String getFilterBy() {
+ return getParameter(FILTER_BY);
+ }
+
+ public PersonService.FilterOperation getFilterOperation() {
+ String filterOp = getParameter(FILTER_OPERATION);
+ try {
+ return filterOp == null
+ ? PersonService.FilterOperation.contains
+ : PersonService.FilterOperation.valueOf(filterOp);
+ } catch (IllegalArgumentException iae) {
+ throw new SocialSpiException(ResponseError.BAD_REQUEST,
+ "Parameter " + FILTER_OPERATION + " (" + filterOp + ") is not valid.");
+ }
+ }
+
+ public String getFilterValue() {
+ String filterValue = getParameter(FILTER_VALUE);
+ return filterValue == null ? "" : filterValue;
+ }
+
+ public Set<String> getFields() {
+ return getFields(Collections.<String>emptySet());
+ }
+
+ public Set<String> getFields(Set<String> defaultValue) {
+ Set<String> result = ImmutableSet.copyOf(getListParameter(FIELDS));
+ if (result.isEmpty()) {
+ return defaultValue;
+ }
+ return result;
+ }
+
+
+ public SecurityToken getToken() {
+ return token;
+ }
+
+ public <T> T getTypedParameter(String parameterName, Class<T> dataTypeClass) {
+ return converter.convertToObject(getParameter(parameterName), dataTypeClass);
+ }
+
+ public String getParameter(String paramName) {
+ Object param = this.parameters.get(paramName);
+ if (param instanceof List) {
+ if (((List)param).isEmpty()) {
+ return null;
+ } else {
+ param = ((List)param).get(0);
+ }
+ }
+ if (param == null) {
+ return null;
+ }
+ return param.toString();
+ }
+
+ public String getParameter(String paramName, String defaultValue) {
+ String param = getParameter(paramName);
+ if (param == null) {
+ return defaultValue;
+ }
+ return param;
+ }
+
+ public List<String> getListParameter(String paramName) {
+ Object param = this.parameters.get(paramName);
+ if (param == null) {
+ return Collections.emptyList();
+ }
+ if (param instanceof String && ((String)param).indexOf(',') != -1) {
+ List<String> listParam = Arrays.asList(((String)param).split(","));
+ this.parameters.put(paramName, listParam);
+ return listParam;
+ }
+ else if (param instanceof List) {
+ List<String> listParam = (List<String>)param;
+ return listParam;
+ } else if (param instanceof JSONArray) {
+ try {
+ JSONArray jsonArray = (JSONArray)param;
+ List<String> returnVal = Lists.newArrayListWithExpectedSize(jsonArray.length());
+ for (int i = 0; i < jsonArray.length(); i++) {
+ returnVal.add(jsonArray.getString(i));
+ }
+ return returnVal;
+ } catch (JSONException je) {
+ throw new SocialSpiException(ResponseError.BAD_REQUEST, je.getMessage(), je);
+ }
+ } else {
+ // Allow up-conversion of non-array to array params.
+ return Lists.newArrayList(param.toString());
+ }
+ }
+
+
+ // Exposed for testing only
+ void setParameter(String paramName, Object paramValue) {
+ if (paramValue instanceof String[]) {
+ String[] arr = (String[])paramValue;
+ if (arr.length == 1) {
+ this.parameters.put(paramName, arr[0]);
+ } else {
+ this.parameters.put(paramName, Lists.newArrayList(arr));
+ }
+ } else if (paramValue instanceof String) {
+ String stringValue = (String)paramValue;
+ if (stringValue.length() > 0) {
+ this.parameters.put(paramName, stringValue);
+ }
+ } else {
+ this.parameters.put(paramName, paramValue);
+ }
+ }
+}
Modified: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/DataServiceServlet.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/DataServiceServlet.java?rev=740067&r1=740066&r2=740067&view=diff
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/DataServiceServlet.java (original)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/DataServiceServlet.java Mon Feb 2 18:39:12 2009
@@ -18,46 +18,32 @@
package org.apache.shindig.social.opensocial.service;
import org.apache.shindig.auth.SecurityToken;
-import org.apache.shindig.config.ContainerConfig;
-import org.apache.shindig.social.ResponseError;
import org.apache.shindig.social.opensocial.spi.DataCollection;
import org.apache.shindig.social.opensocial.spi.RestfulCollection;
-import com.google.common.base.Objects;
import com.google.common.collect.ImmutableMap;
-import com.google.inject.Inject;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
-import java.util.List;
-import java.util.Map;
+import java.io.Reader;
+import java.util.concurrent.Future;
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";
protected static final String ATOM_FORMAT = "atom";
protected static final String XML_FORMAT = "xml";
- public static final String PEOPLE_ROUTE = "people";
- public static final String ACTIVITY_ROUTE = "activities";
- public static final String APPDATA_ROUTE = "appdata";
-
public static final String CONTENT_TYPE = "CONTENT_TYPE";
private static final Logger logger = Logger.getLogger("org.apache.shindig.social.opensocial.spi");
- /** Map from service name to the property name in the container config */
- private static final Map<String, String> SERVICE_TO_SUPPORTED_FIELD_MAP =
- ImmutableMap.of("people", "person", "activities", "activity");
-
-
- private ContainerConfig config;
+ protected static final String X_HTTP_METHOD_OVERRIDE = "X-HTTP-Method-Override";
@Override
protected void doGet(HttpServletRequest servletRequest,
@@ -106,26 +92,37 @@
responseItem.getErrorMessage());
}
- @Inject
- public void setContainerConfig(ContainerConfig config) {
- this.config = config;
- }
-
/**
* Handler for non-batch requests.
*/
private void handleSingleRequest(HttpServletRequest servletRequest,
HttpServletResponse servletResponse, SecurityToken token,
BeanConverter converter) throws IOException {
- RestfulRequestItem requestItem = new RestfulRequestItem(servletRequest, token, converter);
- ResponseItem responseItem;
- if (requestItem.getUrl().endsWith("/@supportedFields")) {
- responseItem = getSupportedFields(requestItem);
- } else {
- responseItem = getResponseItem(handleRequestItem(requestItem, servletRequest));
+ // TODO Rework to allow sub-services
+ String path = servletRequest.getPathInfo();
+
+ // TODO - This shouldnt be on BaseRequestItem
+ String method = servletRequest.getParameter(X_HTTP_METHOD_OVERRIDE);
+ if (method == null) {
+ method = servletRequest.getMethod();
+ }
+
+ // Always returns a non-null handler.
+ RestHandler handler = dispatcher.getRestHandler(path, method.toUpperCase());
+
+ Reader bodyReader = null;
+ if (!servletRequest.getMethod().equals("GET") &&
+ !servletRequest.getMethod().equals("HEAD")) {
+ bodyReader = servletRequest.getReader();
}
+ // Execute the request
+ Future future = handler.execute(path, servletRequest.getParameterMap(),
+ bodyReader, token, converter);
+
+ ResponseItem responseItem = getResponseItem(future);
+
servletResponse.setContentType(converter.getContentType());
if (responseItem.getError() == null) {
PrintWriter writer = servletResponse.getWriter();
@@ -141,25 +138,6 @@
}
}
- private ResponseItem getSupportedFields(RequestItem requestItem) {
- String service = requestItem.getService();
- String configProperty = SERVICE_TO_SUPPORTED_FIELD_MAP.get(service);
- if (configProperty == null) {
- configProperty = service;
- }
-
- String container = Objects.firstNonNull(requestItem.getToken().getContainer(), "default");
- // TODO: hardcoding opensocial-0.8 is brittle
- List<Object> fields = config.getList(container,
- "${gadgets\\.features.opensocial-0\\.8.supportedFields." + configProperty + '}');
-
- if (fields.size() == 0) {
- return new ResponseItem(ResponseError.NOT_IMPLEMENTED,"Supported fields not available for" +
- " service \"" + service + '\"');
- }
-
- return new ResponseItem(fields);
- }
BeanConverter getConverterForRequest(HttpServletRequest servletRequest) {
String formatString = null;
Copied: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/DefaultHandlerRegistry.java (from r738302, incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/StandardHandlerDispatcher.java)
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/DefaultHandlerRegistry.java?p2=incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/DefaultHandlerRegistry.java&p1=incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/StandardHandlerDispatcher.java&r1=738302&r2=740067&rev=740067&view=diff
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/StandardHandlerDispatcher.java (original)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/DefaultHandlerRegistry.java Mon Feb 2 18:39:12 2009
@@ -18,61 +18,291 @@
*/
package org.apache.shindig.social.opensocial.service;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.common.util.ImmediateFuture;
+import org.apache.shindig.social.ResponseError;
+import org.apache.shindig.social.opensocial.spi.SocialSpiException;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.name.Named;
+import org.json.JSONException;
+import org.json.JSONObject;
+import java.io.Reader;
+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.concurrent.Future;
/**
- * Default implementation of HandlerDispatcher. Provides default
- * bindings for the person, activity, and app data handlers.
+ * Default implementation of HandlerRegistry. Bind to appropriately
+ * annotated handlers.
*/
-public class StandardHandlerDispatcher implements HandlerDispatcher {
- private final Map<String, Provider<? extends DataRequestHandler>> handlers;
+public class DefaultHandlerRegistry implements HandlerRegistry {
+
+ private final Map<String, RestHandler> restOperations = Maps.newHashMap();
+ private final Map<String, RpcHandler> rpcOperations = Maps.newHashMap();
+
+ private final Injector injector;
/**
- * Creates a dispatcher with the standard handlers.
- * @param personHandlerProvider provider for the person handler
- * @param activityHandlerProvider provider for the activity handler
- * @param appDataHandlerProvider provider for the app data handler
+ * 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 StandardHandlerDispatcher(Provider<PersonHandler> personHandlerProvider,
- Provider<ActivityHandler> activityHandlerProvider,
- Provider<AppDataHandler> appDataHandlerProvider) {
- this(ImmutableMap.of(
- DataServiceServlet.PEOPLE_ROUTE, personHandlerProvider,
- DataServiceServlet.ACTIVITY_ROUTE, activityHandlerProvider,
- DataServiceServlet.APPDATA_ROUTE, appDataHandlerProvider));
+ public DefaultHandlerRegistry(Injector injector,
+ @Named("org.apache.shindig.handlers") List handlers) {
+ this.injector = injector;
+ addHandlers(handlers.toArray());
}
/**
- * Creates a dispatcher with a custom list of handlers.
- * @param handlers a map of handlers by service name
+ * Get an RPC handler
*/
- public StandardHandlerDispatcher(Map<String,Provider<? extends DataRequestHandler>> handlers) {
- this.handlers = Maps.newHashMap(handlers);
+ public RpcHandler getRpcHandler(JSONObject rpc) {
+ try {
+ String key = rpc.getString("method");
+ RpcHandler rpcHandler = rpcOperations.get(key);
+ if (rpcHandler == null) {
+ return new ErrorRpcHandler(new SocialSpiException(ResponseError.NOT_IMPLEMENTED,
+ "The method " + key + " is not implemented"));
+ }
+ return rpcHandler;
+ } catch (JSONException je) {
+ return new ErrorRpcHandler(new SocialSpiException(ResponseError.BAD_REQUEST,
+ "No method requested in RPC"));
+ }
}
/**
- * Gets a handler by service name.
+ * Get a REST request handler
*/
- public DataRequestHandler getHandler(String service) {
- Provider<? extends DataRequestHandler> provider = handlers.get(service);
- if (provider == null) {
- return null;
+ 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 SocialSpiException(ResponseError.NOT_IMPLEMENTED,
+ "No service defined for path " + path));
}
+ return restHandler;
+ }
- return provider.get();
+ public Set<String> getSupportedRestServices() {
+ return Collections.unmodifiableSet(restOperations.keySet());
+ }
+
+ public Set<String> getSupportedRpcServices() {
+ return Collections.unmodifiableSet(rpcOperations.keySet());
}
/**
* Adds a custom handler.
*/
- public void addHandler(String service, Provider<? extends DataRequestHandler> handler) {
- handlers.put(service, handler);
+ public void addHandlers(Object... handlers) {
+ for (Object handler : handlers) {
+ Class handlerType;
+ if (handler instanceof Class) {
+ handlerType = (Class) handler;
+ handler = injector.getInstance(handlerType);
+ } else {
+ handlerType = handler.getClass();
+ }
+ if (!handlerType.isAnnotationPresent(Service.class)) {
+ throw new IllegalStateException("Attempt to bind unannotated service implementation " +
+ handlerType.getName());
+ }
+ Service service = (Service) handlerType.getAnnotation(Service.class);
+ String serviceName = service.name();
+
+ for (Method m : handlerType.getMethods()) {
+ if (m.isAnnotationPresent(Operation.class)) {
+ Operation op = m.getAnnotation(Operation.class);
+ createRpcHandler(handler, service, m);
+ createRestHandler(handler, service, op, m);
+ }
+ }
+ }
+ }
+
+ private void createRestHandler(Object handler, Service service, Operation op, Method m) {
+ RestHandler restHandler = new RestInvocationHandler(service, op, m, handler);
+ String serviceName = service.name();
+
+ for (String httpMethod : op.httpMethods()) {
+ if (!StringUtils.isEmpty(httpMethod)) {
+ if (StringUtils.isEmpty(op.path())) {
+ // Use the standard service name as the key
+ restOperations.put(httpMethod.toUpperCase() + " /" + serviceName, 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);
+ }
+ }
+ }
+ }
+
+ private void createRpcHandler(Object handler, Service service, Method m) {
+ RpcHandler rpcHandler = new RpcInvocationHandler(m, handler);
+ String defaultName = service.name() + "." + m.getName();
+ rpcOperations.put(defaultName, rpcHandler);
+ }
+
+ /**
+ * 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 {
+ private Method receiver;
+ Object handlerInstance;
+
+ private RpcInvocationHandler(Method receiver, Object handlerInstance) {
+ this.receiver = receiver;
+ this.handlerInstance = handlerInstance;
+ }
+
+ public Future execute(JSONObject rpc, SecurityToken token, BeanConverter converter) {
+ try {
+ RequestItem item;
+ if (rpc.has("params")) {
+ item = new BaseRequestItem((JSONObject)rpc.get("params"), token, converter);
+ } else {
+ item = new BaseRequestItem(new JSONObject(), token, converter);
+ }
+ Object result = receiver.invoke(handlerInstance, item);
+ if (result instanceof Future) {
+ return (Future)result;
+ }
+ return ImmediateFuture.newInstance(result);
+ } catch (InvocationTargetException ite) {
+ // Unwrap these
+ return ImmediateFuture.errorInstance(ite.getTargetException());
+ } catch (Exception e) {
+ return ImmediateFuture.errorInstance(e);
+ }
+ }
+ }
+
+ /**
+ * 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 {
+ Method receiver;
+ Object handlerInstance;
+ Service service;
+ Operation operation;
+ final String[] expectedUrl;
+
+ private RestInvocationHandler(Service service, Operation operation,
+ Method receiver, Object handlerInstance) {
+ this.service = service;
+ this.operation = operation;
+ this.receiver = receiver;
+ this.handlerInstance = handlerInstance;
+ expectedUrl = service.path().split("/");
+ }
+
+ public Future<?> execute(String path, Map<String, String[]> parameters, Reader body,
+ SecurityToken token, BeanConverter converter) {
+ // Create a mutable copy.
+ parameters = new HashMap(parameters);
+ try {
+ // bind the body contents if available
+ if (body != null) {
+ parameters.put(operation.bodyParam(), new String[]{IOUtils.toString(body)});
+ }
+
+ extractPathParameters(parameters, path);
+
+ RequestItem item = new BaseRequestItem(parameters, token, converter);
+ Object result = receiver.invoke(handlerInstance, item);
+ if (result instanceof Future) {
+ return (Future) result;
+ }
+ return ImmediateFuture.newInstance(result);
+ } catch (InvocationTargetException ite) {
+ // Unwrap these
+ return ImmediateFuture.errorInstance(ite.getTargetException());
+ } catch (Exception e) {
+ return ImmediateFuture.errorInstance(e);
+ }
+ }
+
+ private void extractPathParameters(Map<String, String[]> parameters, String path) {
+ String[] actualUrl = path.split("/");
+
+ 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 SocialSpiException(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});
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Standard REST handler to wrap errors
+ */
+ private static final class ErrorRestHandler implements RestHandler {
+
+ private SocialSpiException error;
+
+ public ErrorRestHandler(SocialSpiException error) {
+ this.error = error;
+ }
+
+ public Future execute(String path, Map parameters, Reader body,
+ SecurityToken token, BeanConverter converter) {
+ return ImmediateFuture.errorInstance(error);
+ }
+ }
+
+ /**
+ * Standard RPC handler to wrap errors
+ */
+ private static final class ErrorRpcHandler implements RpcHandler {
+
+ private SocialSpiException error;
+
+ public ErrorRpcHandler(SocialSpiException error) {
+ this.error = error;
+ }
+
+ public Future execute(JSONObject rpc, SecurityToken token, BeanConverter converter) {
+ return ImmediateFuture.errorInstance(error);
+ }
}
}
Propchange: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/DefaultHandlerRegistry.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/HandlerPreconditions.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/HandlerPreconditions.java?rev=740067&view=auto
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/HandlerPreconditions.java (added)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/HandlerPreconditions.java Mon Feb 2 18:39:12 2009
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shindig.social.opensocial.service;
+
+import org.apache.shindig.social.ResponseError;
+import org.apache.shindig.social.opensocial.spi.SocialSpiException;
+
+import java.util.Collection;
+
+/**
+ * Utility class for common API call preconditions
+ */
+public class HandlerPreconditions {
+
+ private HandlerPreconditions() {}
+
+ public static void requireNotEmpty(Collection<?> coll, String message)
+ throws SocialSpiException {
+ if (coll.isEmpty()) {
+ throw new SocialSpiException(ResponseError.BAD_REQUEST, message);
+ }
+ }
+
+ public static void requireEmpty(Collection<?> coll, String message) throws SocialSpiException {
+ if (!coll.isEmpty()) {
+ throw new SocialSpiException(ResponseError.BAD_REQUEST, message);
+ }
+ }
+
+ public static void requireSingular(Collection<?> coll, String message)
+ throws SocialSpiException {
+ if (coll.size() != 1) {
+ throw new SocialSpiException(ResponseError.BAD_REQUEST, message);
+ }
+ }
+
+ public static void requirePlural(Collection<?> coll, String message) throws SocialSpiException {
+ if (coll.size() <= 1) {
+ throw new SocialSpiException(ResponseError.BAD_REQUEST, message);
+ }
+ }
+}
Copied: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/HandlerRegistry.java (from r738302, incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/HandlerDispatcher.java)
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/HandlerRegistry.java?p2=incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/HandlerRegistry.java&p1=incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/HandlerDispatcher.java&r1=738302&r2=740067&rev=740067&view=diff
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/HandlerDispatcher.java (original)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/HandlerRegistry.java Mon Feb 2 18:39:12 2009
@@ -19,19 +19,36 @@
package org.apache.shindig.social.opensocial.service;
import com.google.inject.ImplementedBy;
+import org.json.JSONObject;
+
+import java.util.Set;
/**
- * Dispatcher for DataRequestHandler requests. The default implementation
- * registers the three standard Shindig handlers.
- * <p>
- * Add a custom binding of this interface to customize request handling.
+ * Registry of REST and RPC handlers for the set of available services
*/
-@ImplementedBy(StandardHandlerDispatcher.class)
-public interface HandlerDispatcher {
+@ImplementedBy(DefaultHandlerRegistry.class)
+public interface HandlerRegistry {
+
+ /**
+ * @param rpc The rpc to dispatch
+ * @return the handler
+ */
+ RpcHandler getRpcHandler(JSONObject rpc);
+
+ /**
+ * @param path Path of the service
+ * @param method The HTTP method
+ * @return the handler
+ */
+ RestHandler getRestHandler(String path, String method);
+
+ /**
+ * @return The list of available services
+ */
+ public Set<String> getSupportedRestServices();
/**
- * @param service a service name
- * @return the handler, or null if no handler is registered for that service
+ * @return The list of available services
*/
- DataRequestHandler getHandler(String service);
+ public Set<String> getSupportedRpcServices();
}
Propchange: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/HandlerRegistry.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/JsonRpcServlet.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/JsonRpcServlet.java?rev=740067&r1=740066&r2=740067&view=diff
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/JsonRpcServlet.java (original)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/JsonRpcServlet.java Mon Feb 2 18:39:12 2009
@@ -17,6 +17,8 @@
*/
package org.apache.shindig.social.opensocial.service;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
import org.apache.shindig.auth.SecurityToken;
import org.apache.shindig.common.util.JsonConversionUtil;
import org.apache.shindig.social.ResponseError;
@@ -24,18 +26,16 @@
import org.apache.shindig.social.opensocial.spi.RestfulCollection;
import com.google.common.collect.Lists;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.StringUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
-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;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.Future;
/**
* JSON-RPC handler servlet.
@@ -101,8 +101,7 @@
// into single requests.
for (int i = 0; i < batch.length(); i++) {
JSONObject batchObj = batch.getJSONObject(i);
- RpcRequestItem requestItem = new RpcRequestItem(batchObj, token, jsonConverter);
- responses.add(handleRequestItem(requestItem, servletRequest));
+ responses.add(dispatcher.getRpcHandler(batchObj).execute(batchObj, token, jsonConverter));
}
// Resolve each Future into a response.
@@ -125,11 +124,13 @@
if (request.has("id")) {
key = request.getString("id");
}
- RpcRequestItem requestItem = new RpcRequestItem(request, token, jsonConverter);
+
+ // getRpcHandler never returns null
+ Future future = dispatcher.getRpcHandler(request).execute(request, token, jsonConverter);
// Resolve each Future into a response.
// TODO: should use shared deadline across each request
- ResponseItem response = getResponseItem(handleRequestItem(requestItem, servletRequest));
+ ResponseItem response = getResponseItem(future);
JSONObject result = getJSONResponse(key, response);
servletResponse.getWriter().write(result.toString());
}
@@ -143,15 +144,15 @@
result.put("error", getErrorJson(responseItem));
} else {
Object response = responseItem.getResponse();
- JSONObject converted = (JSONObject) jsonConverter.convertToJson(response);
+ Object converted = jsonConverter.convertToJson(response);
if (response instanceof RestfulCollection) {
// FIXME this is a little hacky because of the field names in the RestfulCollection
- converted.put("list", converted.remove("entry"));
+ ((JSONObject)converted).put("list", ((JSONObject)converted).remove("entry"));
result.put("data", converted);
} else if (response instanceof DataCollection) {
- if (converted.has("entry")) {
- result.put("data", converted.get("entry"));
+ if (((JSONObject)converted).has("entry")) {
+ result.put("data", ((JSONObject)converted).get("entry"));
}
} else {
result.put("data", converted);
Added: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/Operation.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/Operation.java?rev=740067&view=auto
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/Operation.java (added)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/Operation.java Mon Feb 2 18:39:12 2009
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.apache.shindig.social.opensocial.service;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a method on a ServiceHandler which expose a REST/RPC operation
+ * The name of the annotated method is the literal name of the method for JSON-RPC
+ *
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Operation {
+ /**
+ * The HTTP methods to bind this operation to.
+ */
+ String[] httpMethods();
+
+ /**
+ * The parameter name to bind the body content to in the RequestItem
+ * passed to the REST/RPC handler.
+ */
+ String bodyParam() default "body";
+
+ /**
+ * The path to match for the operation to override the service
+ * path matching and parameter binding. This is useful for situations
+ * such as /<service>/@supportedFields where the path determines the
+ * operation rather than the HTTP method in REST
+ */
+ String path() default "";
+}
Modified: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/PersonHandler.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/PersonHandler.java?rev=740067&r1=740066&r2=740067&view=diff
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/PersonHandler.java (original)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/PersonHandler.java Mon Feb 2 18:39:12 2009
@@ -17,7 +17,7 @@
*/
package org.apache.shindig.social.opensocial.service;
-import org.apache.shindig.social.ResponseError;
+import org.apache.shindig.config.ContainerConfig;
import org.apache.shindig.social.opensocial.model.Person;
import org.apache.shindig.social.opensocial.spi.CollectionOptions;
import org.apache.shindig.social.opensocial.spi.GroupId;
@@ -25,35 +25,26 @@
import org.apache.shindig.social.opensocial.spi.SocialSpiException;
import org.apache.shindig.social.opensocial.spi.UserId;
-import com.google.common.collect.Sets;
+import com.google.common.base.Objects;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
import com.google.inject.Inject;
+
+import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
+import java.util.logging.Logger;
-public class PersonHandler extends DataRequestHandler {
+@Service(name = "people", path = "/{userId}+/{groupId}/{personId}+")
+public class PersonHandler {
+ private final static Logger logger = Logger.getLogger(PersonHandler.class.getName());
private final PersonService personService;
-
- private static final String PEOPLE_PATH = "/people/{userId}+/{groupId}/{personId}+";
+ private final ContainerConfig config;
@Inject
- public PersonHandler(PersonService personService) {
+ public PersonHandler(PersonService personService, ContainerConfig config) {
this.personService = personService;
- }
-
- @Override
- protected Future<?> handleDelete(RequestItem request) throws SocialSpiException {
- throw new SocialSpiException(ResponseError.BAD_REQUEST, "You can't delete people.");
- }
-
- @Override
- protected Future<?> handlePut(RequestItem request) throws SocialSpiException {
- throw new SocialSpiException(ResponseError.NOT_IMPLEMENTED, "You can't update right now.");
- }
-
- @Override
- protected Future<?> handlePost(RequestItem request) throws SocialSpiException {
- throw new SocialSpiException(ResponseError.NOT_IMPLEMENTED, "You can't add people right now.");
+ this.config = config;
}
/**
@@ -61,16 +52,15 @@
*
* examples: /people/john.doe/@all /people/john.doe/@friends /people/john.doe/@self
*/
- @Override
- protected Future<?> handleGet(RequestItem request) throws SocialSpiException {
- request.applyUrlTemplate(PEOPLE_PATH);
+ @Operation(httpMethods = "GET")
+ public Future<?> get(RequestItem request) throws SocialSpiException {
GroupId groupId = request.getGroup();
Set<String> optionalPersonId = ImmutableSet.copyOf(request.getListParameter("personId"));
Set<String> fields = request.getFields(Person.Field.DEFAULT_FIELDS);
Set<UserId> userIds = request.getUsers();
// Preconditions
- Preconditions.requireNotEmpty(userIds, "No userId specified");
+ HandlerPreconditions.requireNotEmpty(userIds, "No userId specified");
if (userIds.size() > 1 && !optionalPersonId.isEmpty()) {
throw new IllegalArgumentException("Cannot fetch personIds for multiple userIds");
}
@@ -103,4 +93,12 @@
// Every other case is a collection response.
return personService.getPeople(userIds, groupId, options, fields, request.getToken());
}
+
+ @Operation(httpMethods = "GET", path="/@supportedFields")
+ public List supportedFields(RequestItem request) {
+ // TODO: Would be nice if name in config matched name of service.
+ String container = Objects.firstNonNull(request.getToken().getContainer(), "default");
+ return config.getList(container,
+ "${gadgets\\.features.opensocial-0\\.8.supportedFields.person}");
+ }
}
Modified: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/RequestItem.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/RequestItem.java?rev=740067&r1=740066&r2=740067&view=diff
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/RequestItem.java (original)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/RequestItem.java Mon Feb 2 18:39:12 2009
@@ -18,205 +18,68 @@
package org.apache.shindig.social.opensocial.service;
import org.apache.shindig.auth.SecurityToken;
-import org.apache.shindig.social.ResponseError;
import org.apache.shindig.social.opensocial.spi.GroupId;
import org.apache.shindig.social.opensocial.spi.PersonService;
-import org.apache.shindig.social.opensocial.spi.SocialSpiException;
import org.apache.shindig.social.opensocial.spi.UserId;
-import org.joda.time.DateTime;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-
-import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Set;
/**
- * Abstract base type for social API requests.
+ * A request to pass to a bound service handler
*/
-public abstract class RequestItem {
+public interface RequestItem {
// Common OpenSocial API fields
- public static final String APP_ID = "appId";
+ String APP_ID = "appId";
+ String USER_ID = "userId";
+ String GROUP_ID = "groupId";
+ String START_INDEX = "startIndex";
+ String COUNT = "count";
+ String SORT_BY = "sortBy";
+ String SORT_ORDER = "sortOrder";
+ String FILTER_BY = "filterBy";
+ String FILTER_OPERATION = "filterOp";
+ String FILTER_VALUE = "filterValue";
+ String FIELDS = "fields";// Opensocial defaults
+ int DEFAULT_START_INDEX = 0;
+ int DEFAULT_COUNT = 20;
+ String APP_SUBSTITUTION_TOKEN = "@app";
+
+ String getAppId();
+
+ Date getUpdatedSince();
+
+ Set<UserId> getUsers();
+
+ GroupId getGroup();
+
+ int getStartIndex();
+
+ int getCount();
+
+ String getSortBy();
+
+ PersonService.SortOrder getSortOrder();
+
+ String getFilterBy();
+
+ PersonService.FilterOperation getFilterOperation();
- public static final String USER_ID = "userId";
+ String getFilterValue();
- public static final String GROUP_ID = "groupId";
+ Set<String> getFields();
- public static final String START_INDEX = "startIndex";
+ Set<String> getFields(Set<String> defaultValue);
- public static final String COUNT = "count";
-
- public static final String SORT_BY = "sortBy";
- public static final String SORT_ORDER = "sortOrder";
-
- public static final String FILTER_BY = "filterBy";
- public static final String FILTER_OPERATION = "filterOp";
- public static final String FILTER_VALUE = "filterValue";
-
- public static final String FIELDS = "fields";
-
- // Opensocial defaults
- public static final int DEFAULT_START_INDEX = 0;
-
- public static final int DEFAULT_COUNT = 20;
-
- public static final String APP_SUBSTITUTION_TOKEN = "@app";
-
- private final SecurityToken token;
-
- protected final BeanConverter converter;
-
- private final String operation;
-
- private final String service;
-
- public RequestItem(String service, String operation, SecurityToken token,
- BeanConverter converter) {
- this.service = service;
- this.operation = operation;
- this.token = token;
- this.converter = converter;
- }
-
- public String getAppId() {
- String appId = getParameter(APP_ID);
- if (appId != null && appId.equals(APP_SUBSTITUTION_TOKEN)) {
- return token.getAppId();
- } else {
- return appId;
- }
- }
-
- public Date getUpdatedSince() {
- String updatedSince = getParameter("updatedSince");
- if (updatedSince == null)
- return null;
-
- DateTime date = new DateTime(updatedSince);
- if (date == null) return null;
-
- return date.toDate();
- }
-
- public Set<UserId> getUsers() {
- List<String> ids = getListParameter(USER_ID);
- if (ids.isEmpty()) {
- if (token.getViewerId() != null) {
- // Assume @me
- ids = Lists.newArrayList("@me");
- } else {
- throw new IllegalArgumentException("No userId provided and viewer not available");
- }
- }
- Set<UserId> userIds = Sets.newLinkedHashSet();
- for (String id : ids) {
- userIds.add(UserId.fromJson(id));
- }
- return userIds;
- }
-
-
- public GroupId getGroup() {
- return GroupId.fromJson(getParameter(GROUP_ID, "@self"));
- }
-
- public int getStartIndex() {
- String startIndex = getParameter(START_INDEX);
- try {
- return startIndex == null ? DEFAULT_START_INDEX
- : Integer.valueOf(startIndex);
- } catch (NumberFormatException nfe) {
- throw new SocialSpiException(ResponseError.BAD_REQUEST,
- "Parameter " + START_INDEX + " (" + startIndex + ") is not a number.");
- }
- }
-
- public int getCount() {
- String count = getParameter(COUNT);
- try {
- return count == null ? DEFAULT_COUNT : Integer.valueOf(count);
- } catch (NumberFormatException nfe) {
- throw new SocialSpiException(ResponseError.BAD_REQUEST,
- "Parameter " + COUNT + " (" + count + ") is not a number.");
- }
- }
-
- public String getSortBy() {
- String sortBy = getParameter(SORT_BY);
- return sortBy == null ? PersonService.TOP_FRIENDS_SORT : sortBy;
- }
-
- public PersonService.SortOrder getSortOrder() {
- String sortOrder = getParameter(SORT_ORDER);
- try {
- return sortOrder == null
- ? PersonService.SortOrder.ascending
- : PersonService.SortOrder.valueOf(sortOrder);
- } catch (IllegalArgumentException iae) {
- throw new SocialSpiException(ResponseError.BAD_REQUEST,
- "Parameter " + SORT_ORDER + " (" + sortOrder + ") is not valid.");
- }
- }
-
- public String getFilterBy() {
- return getParameter(FILTER_BY);
- }
-
- public PersonService.FilterOperation getFilterOperation() {
- String filterOp = getParameter(FILTER_OPERATION);
- try {
- return filterOp == null
- ? PersonService.FilterOperation.contains
- : PersonService.FilterOperation.valueOf(filterOp);
- } catch (IllegalArgumentException iae) {
- throw new SocialSpiException(ResponseError.BAD_REQUEST,
- "Parameter " + FILTER_OPERATION + " (" + filterOp + ") is not valid.");
- }
- }
-
- public String getFilterValue() {
- String filterValue = getParameter(FILTER_VALUE);
- return filterValue == null ? "" : filterValue;
- }
-
- public Set<String> getFields() {
- return getFields(Collections.<String>emptySet());
- }
-
- public Set<String> getFields(Set<String> defaultValue) {
- Set<String> result = ImmutableSet.copyOf(getListParameter(FIELDS));
- if (result.isEmpty()) {
- return defaultValue;
- }
- return result;
- }
-
- public String getOperation() {
- return operation;
- }
-
- public String getService() {
- return service;
- }
-
- public SecurityToken getToken() {
- return token;
- }
-
- public abstract <T> T getTypedParameter(String parameterName, Class<T> dataTypeClass);
-
- public abstract <T> T getTypedParameters(Class<T> dataTypeClass);
+ SecurityToken getToken();
- public abstract void applyUrlTemplate(String urlTemplate);
+ <T> T getTypedParameter(String parameterName, Class<T> dataTypeClass);
- public abstract String getParameter(String paramName);
+ String getParameter(String paramName);
- public abstract String getParameter(String paramName, String defaultValue);
+ String getParameter(String paramName, String defaultValue);
- public abstract List<String> getListParameter(String paramName);
+ List<String> getListParameter(String paramName);
}
Added: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/RestHandler.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/RestHandler.java?rev=740067&view=auto
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/RestHandler.java (added)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/RestHandler.java Mon Feb 2 18:39:12 2009
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shindig.social.opensocial.service;
+
+import org.apache.shindig.auth.SecurityToken;
+
+import java.io.Reader;
+import java.util.Map;
+import java.util.concurrent.Future;
+
+/**
+ * Interface exposed by a REST handler
+ */
+public interface RestHandler {
+
+ /**
+ * 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,
+ SecurityToken token, BeanConverter converter);
+}
Added: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/RpcHandler.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/RpcHandler.java?rev=740067&view=auto
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/RpcHandler.java (added)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/RpcHandler.java Mon Feb 2 18:39:12 2009
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shindig.social.opensocial.service;
+
+import org.apache.shindig.auth.SecurityToken;
+
+import org.json.JSONObject;
+
+import java.util.concurrent.Future;
+
+/**
+ * Interface exposed by an RPC handler
+ */
+public interface RpcHandler {
+
+ /**
+ * Handle the request and return a Future from which the response object
+ * can be retrieved
+ */
+ Future<?> execute(JSONObject rpc, SecurityToken st, BeanConverter converter);
+}
Added: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/Service.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/Service.java?rev=740067&view=auto
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/Service.java (added)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/Service.java Mon Feb 2 18:39:12 2009
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shindig.social.opensocial.service;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates the base path for REST calls or the RPC service name a RequestHandler
+ * can dispatch to. Define parameter binding for REST path variables
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface Service {
+ /**
+ * The name of the service this handler exports. This is also the name of the
+ * root path element of the REST endpoint. E.g. The "activities" service
+ * consumes all paths under /activities/...
+ */
+ String name();
+
+ /**
+ * The structure of the REST paths used to address this service. Paths
+ * can contain placeholders delimited by {...} that bind a named parameter or
+ * set of parameters to the request. A plural parameter is denoted by appending '+'
+ * after the named parameter. Parameters are bound in order left-to-right and
+ * missing path segments are bound to null or empty sets
+ *
+ * E.g.
+ *
+ * /{userId}+/{group}/{personId}+ will parameterize the following URLs
+ * /1/@self => { userId : [1], group : @self, personId : []}
+ * /1/@self => { userId : [1], group : @self, personId : []}
+ * /1,2/@friends => { userId : [1,2], group : @friends, personId : []}
+ * /1,2/@friends/2,3 => { userId : [1,2], group : @friends, personId : [2,3]}
+ *
+ */
+ String path() default "";
+}
Added: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/SystemHandler.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/SystemHandler.java?rev=740067&view=auto
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/SystemHandler.java (added)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/SystemHandler.java Mon Feb 2 18:39:12 2009
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shindig.social.opensocial.service;
+
+import org.apache.shindig.social.opensocial.spi.PersonService;
+
+import com.google.inject.Inject;
+
+import java.util.Set;
+
+
+/**
+ * Implements the 'system' service operations for JSON/XML RPC
+ */
+@Service(name = "system")
+public class SystemHandler {
+
+ HandlerRegistry registry;
+
+ @Inject
+ public SystemHandler(HandlerRegistry registry) {
+ this.registry = registry;
+ }
+
+ @Operation(httpMethods = "GET")
+ public Set<String> listMethods(RequestItem request) {
+ if ("protocol".equalsIgnoreCase(request.getFilterBy()) &&
+ PersonService.FilterOperation.equals == request.getFilterOperation() &&
+ "REST".equalsIgnoreCase(request.getFilterValue())) {
+ return registry.getSupportedRestServices();
+ }
+ return registry.getSupportedRpcServices();
+ }
+}
Modified: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/service/SampleContainerHandler.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/service/SampleContainerHandler.java?rev=740067&r1=740066&r2=740067&view=diff
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/service/SampleContainerHandler.java (original)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/service/SampleContainerHandler.java Mon Feb 2 18:39:12 2009
@@ -18,57 +18,48 @@
package org.apache.shindig.social.sample.service;
-import com.google.inject.Inject;
-
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.shindig.common.util.ImmediateFuture;
import org.apache.shindig.social.ResponseError;
-import org.apache.shindig.social.opensocial.service.DataRequestHandler;
+import org.apache.shindig.social.opensocial.service.Operation;
import org.apache.shindig.social.opensocial.service.RequestItem;
+import org.apache.shindig.social.opensocial.service.Service;
import org.apache.shindig.social.opensocial.spi.SocialSpiException;
import org.apache.shindig.social.sample.spi.JsonDbOpensocialService;
+
+import com.google.inject.Inject;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.concurrent.Future;
-public class SampleContainerHandler extends DataRequestHandler {
+@Service(name = "samplecontainer", path = "/samplecontainer/{type}/{doevil}")
+public class SampleContainerHandler {
private final JsonDbOpensocialService service;
- private static final String POST_PATH = "/samplecontainer/{type}/{doevil}";
-
@Inject
public SampleContainerHandler(JsonDbOpensocialService dbService) {
this.service = dbService;
}
/**
- * We don't support any delete methods right now.
- */
- @Override
- protected Future<?> handleDelete(RequestItem request) throws SocialSpiException {
- throw new SocialSpiException(ResponseError.NOT_IMPLEMENTED, null);
- }
-
- /**
* We don't distinguish between put and post for these urls.
*/
- @Override
- protected Future<?> handlePut(RequestItem request) throws SocialSpiException {
- return handlePost(request);
+ @Operation(httpMethods = "PUT")
+ public Future<?> update(RequestItem request) throws SocialSpiException {
+ return create(request);
}
/**
* Handles /samplecontainer/setstate and /samplecontainer/setevilness/{doevil}. TODO(doll): These
* urls aren't very resty. Consider changing the samplecontainer.html calls post.
*/
- @Override
- protected Future<?> handlePost(RequestItem request) throws SocialSpiException {
- request.applyUrlTemplate(POST_PATH);
+ @Operation(httpMethods = "POST")
+ public Future<?> create(RequestItem request) throws SocialSpiException {
String type = request.getParameter("type");
if (type.equals("setstate")) {
try {
@@ -89,8 +80,8 @@
/**
* Handles /samplecontainer/dumpstate
*/
- @Override
- protected Future<?> handleGet(RequestItem request) {
+ @Operation(httpMethods = "GET")
+ public Future<?> get(RequestItem request) {
return ImmediateFuture.newInstance(service.getDb());
}