From 54ba11210f0657d1b77e75122557d22b5ac58009 Mon Sep 17 00:00:00 2001 From: Inderjeet Singh Date: Thu, 11 Nov 2010 17:21:32 +0000 Subject: [PATCH] Added server-side dispatchers for REST and procedural calls Implemented the REST dispatcher. --- .../gson/example/service/ServicePaths.java | 52 +++++++++ .../gson/wsexample/server/MainServlet.java | 68 ++++-------- .../wsexample/server/ProcedureDispatcher.java | 78 +++++++++++++ .../gson/wsexample/server/RestDispatcher.java | 104 ++++++++++++++++++ .../gson/rest/server/ResponseBuilderMap.java | 57 ++++++++++ .../gson/rest/server/RestResponseBuilder.java | 2 +- 6 files changed, 311 insertions(+), 50 deletions(-) create mode 100644 wsexample/definition/src/main/java/com/google/gson/example/service/ServicePaths.java create mode 100644 wsexample/server/src/main/java/com/google/gson/wsexample/server/ProcedureDispatcher.java create mode 100644 wsexample/server/src/main/java/com/google/gson/wsexample/server/RestDispatcher.java create mode 100644 wsf/src/main/java/com/google/gson/rest/server/ResponseBuilderMap.java diff --git a/wsexample/definition/src/main/java/com/google/gson/example/service/ServicePaths.java b/wsexample/definition/src/main/java/com/google/gson/example/service/ServicePaths.java new file mode 100644 index 00000000..c8c35842 --- /dev/null +++ b/wsexample/definition/src/main/java/com/google/gson/example/service/ServicePaths.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.gson.example.service; + +import com.google.gson.webservice.definition.CallPath; + +/** + * An enum describing all paths for this service + * + * @author Inderjeet Singh + */ +public enum ServicePaths { + NULL_REQUEST(null), + CART("/rest/cart"), + ORDER("/rest/order"); + + private final CallPath path; + + private ServicePaths(String pathInfo) { + this.path = new CallPath(pathInfo); + } + + public CallPath getCallPath() { + return path; + } + + public static CallPath getCallPath(CallPath invokedPath) { + for (ServicePaths path : values()) { + CallPath callPath = path.path; + String callPathInfo = callPath.get(); + // A rest path can end with a resource-id too. + // For example, /rest/cart/1234 should match with /rest/cart + if (callPathInfo != null && invokedPath.matches(callPath)) { + return callPath; + } + } + return null; + } +} \ No newline at end of file diff --git a/wsexample/server/src/main/java/com/google/gson/wsexample/server/MainServlet.java b/wsexample/server/src/main/java/com/google/gson/wsexample/server/MainServlet.java index ffbc62a2..bd9163b4 100644 --- a/wsexample/server/src/main/java/com/google/gson/wsexample/server/MainServlet.java +++ b/wsexample/server/src/main/java/com/google/gson/wsexample/server/MainServlet.java @@ -15,29 +15,12 @@ */ package com.google.gson.wsexample.server; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.example.model.Cart; -import com.google.gson.example.model.Order; -import com.google.gson.example.model.TypedKeys; -import com.google.gson.example.service.SampleJsonService; -import com.google.gson.webservice.definition.HeaderMap; -import com.google.gson.webservice.definition.procedural.RequestBody; -import com.google.gson.webservice.definition.procedural.RequestBodyGsonConverter; -import com.google.gson.webservice.definition.procedural.RequestSpec; -import com.google.gson.webservice.definition.procedural.ResponseBody; -import com.google.gson.webservice.definition.procedural.ResponseBodyGsonConverter; -import com.google.gson.webservice.definition.procedural.ResponseSpec; -import com.google.gson.webservice.definition.procedural.WebServiceCallSpec; -import com.google.gson.webservice.definition.procedural.WebServiceRequest; -import com.google.gson.webservice.definition.procedural.WebServiceResponse; -import com.google.gson.wsf.server.procedural.RequestReceiver; -import com.google.gson.wsf.server.procedural.ResponseSender; - import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import com.google.gson.webservice.definition.CallPath; + /** * An example servlet that receives JSON web-service requests * @@ -45,37 +28,24 @@ import javax.servlet.http.HttpServletResponse; */ @SuppressWarnings("serial") public class MainServlet extends HttpServlet { + private final RestDispatcher restDispatcher; + private final ProcedureDispatcher procedureDispatcher; + + public MainServlet() { + this.restDispatcher = new RestDispatcher(); + this.procedureDispatcher = new ProcedureDispatcher(); + } + @Override public void service(HttpServletRequest req, HttpServletResponse res) { - WebServiceCallSpec spec = SampleJsonService.PLACE_ORDER; - RequestSpec requestSpec = spec.getRequestSpec(); - ResponseSpec responseSpec = spec.getResponseSpec(); - Gson gson = new GsonBuilder() - .registerTypeAdapter(RequestBody.class, - new RequestBodyGsonConverter(requestSpec.getBodySpec())) - .registerTypeAdapter(ResponseBody.class, - new ResponseBodyGsonConverter(responseSpec.getBodySpec())) - .create(); - RequestReceiver requestReceiver = new RequestReceiver(gson, requestSpec); - WebServiceRequest webServiceRequest = requestReceiver.receive(req); - - Cart cart = webServiceRequest.getBody().get(TypedKeys.RequestBody.CART); - String authToken = webServiceRequest.getHeader(TypedKeys.Request.AUTH_TOKEN); - - Order order = placeOrder(cart, authToken); - - // Empty headers per the spec - HeaderMap responseHeaders = new HeaderMap.Builder(responseSpec.getHeadersSpec()).build(); - ResponseBody responseBody = new ResponseBody.Builder(responseSpec.getBodySpec()) - .put(TypedKeys.ResponseBody.ORDER, order) - .build(); - WebServiceResponse response = new WebServiceResponse(responseHeaders, responseBody); - ResponseSender responseSender = new ResponseSender(gson); - responseSender.send(res, response); - } - - private Order placeOrder(Cart cart, String authToken) { - // Create an order, in this case a dummy one. - return new Order(cart, "Order123"); + String servletPath = req.getServletPath(); + int index = "/wsexampleserver".length(); + CallPath callPath = new CallPath(servletPath.substring(index)); + String path = callPath.get(); + if (path.startsWith("/rest")) { + restDispatcher.service(req, res, callPath); + } else if (path.startsWith("/procedure")) { + procedureDispatcher.service(req, res); + } } } diff --git a/wsexample/server/src/main/java/com/google/gson/wsexample/server/ProcedureDispatcher.java b/wsexample/server/src/main/java/com/google/gson/wsexample/server/ProcedureDispatcher.java new file mode 100644 index 00000000..a1b95016 --- /dev/null +++ b/wsexample/server/src/main/java/com/google/gson/wsexample/server/ProcedureDispatcher.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.gson.wsexample.server; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.example.model.Cart; +import com.google.gson.example.model.Order; +import com.google.gson.example.model.TypedKeys; +import com.google.gson.example.service.SampleJsonService; +import com.google.gson.webservice.definition.HeaderMap; +import com.google.gson.webservice.definition.procedural.RequestBody; +import com.google.gson.webservice.definition.procedural.RequestBodyGsonConverter; +import com.google.gson.webservice.definition.procedural.RequestSpec; +import com.google.gson.webservice.definition.procedural.ResponseBody; +import com.google.gson.webservice.definition.procedural.ResponseBodyGsonConverter; +import com.google.gson.webservice.definition.procedural.ResponseSpec; +import com.google.gson.webservice.definition.procedural.WebServiceCallSpec; +import com.google.gson.webservice.definition.procedural.WebServiceRequest; +import com.google.gson.webservice.definition.procedural.WebServiceResponse; +import com.google.gson.wsf.server.procedural.RequestReceiver; +import com.google.gson.wsf.server.procedural.ResponseSender; + +/** + * A dispatcher for all the procedural calls + * + * @author Inderjeet Singh + */ +public final class ProcedureDispatcher { + public void service(HttpServletRequest req, HttpServletResponse res) { + WebServiceCallSpec spec = SampleJsonService.PLACE_ORDER; + RequestSpec requestSpec = spec.getRequestSpec(); + ResponseSpec responseSpec = spec.getResponseSpec(); + Gson gson = new GsonBuilder() + .registerTypeAdapter(RequestBody.class, + new RequestBodyGsonConverter(requestSpec.getBodySpec())) + .registerTypeAdapter(ResponseBody.class, + new ResponseBodyGsonConverter(responseSpec.getBodySpec())) + .create(); + RequestReceiver requestReceiver = new RequestReceiver(gson, requestSpec); + WebServiceRequest webServiceRequest = requestReceiver.receive(req); + + Cart cart = webServiceRequest.getBody().get(TypedKeys.RequestBody.CART); + String authToken = webServiceRequest.getHeader(TypedKeys.Request.AUTH_TOKEN); + + Order order = placeOrder(cart, authToken); + + // Empty headers per the spec + HeaderMap responseHeaders = new HeaderMap.Builder(responseSpec.getHeadersSpec()).build(); + ResponseBody responseBody = new ResponseBody.Builder(responseSpec.getBodySpec()) + .put(TypedKeys.ResponseBody.ORDER, order) + .build(); + WebServiceResponse response = new WebServiceResponse(responseHeaders, responseBody); + ResponseSender responseSender = new ResponseSender(gson); + responseSender.send(res, response); + } + + private Order placeOrder(Cart cart, String authToken) { + // Create an order, in this case a dummy one. + return new Order(cart, "Order123"); + } +} diff --git a/wsexample/server/src/main/java/com/google/gson/wsexample/server/RestDispatcher.java b/wsexample/server/src/main/java/com/google/gson/wsexample/server/RestDispatcher.java new file mode 100644 index 00000000..06a43f6d --- /dev/null +++ b/wsexample/server/src/main/java/com/google/gson/wsexample/server/RestDispatcher.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.gson.wsexample.server; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.example.model.Cart; +import com.google.gson.example.model.Order; +import com.google.gson.example.service.ServicePaths; +import com.google.gson.rest.definition.IDFactory; +import com.google.gson.rest.definition.MetaData; +import com.google.gson.rest.definition.ResourceMap; +import com.google.gson.rest.definition.RestCallSpec; +import com.google.gson.rest.definition.RestRequest; +import com.google.gson.rest.definition.RestResponse; +import com.google.gson.rest.definition.ValueBasedId; +import com.google.gson.rest.server.Repository; +import com.google.gson.rest.server.RepositoryInMemory; +import com.google.gson.rest.server.ResponseBuilderMap; +import com.google.gson.rest.server.RestRequestReceiver; +import com.google.gson.rest.server.RestResponseBuilder; +import com.google.gson.rest.server.RestResponseSender; +import com.google.gson.webservice.definition.CallPath; + +/** + * A dispatcher for all the REST requests + * + * @author Inderjeet Singh + */ +public final class RestDispatcher { + private static final double CURRENT_VERSION = 1D; + private final ResourceMap resourceMap; + private final ResponseBuilderMap responseBuilders; + private final RestCallSpec cartSpec; + private final RestCallSpec orderSpec; + private final Gson gson; + + public RestDispatcher() { + this.cartSpec = new RestCallSpec.Builder(ServicePaths.CART.getCallPath(), Cart.class) + .setVersion(CURRENT_VERSION) + .build(); + this.orderSpec = new RestCallSpec.Builder(ServicePaths.CART.getCallPath(), Cart.class) + .setVersion(CURRENT_VERSION) + .build(); + this.resourceMap = new ResourceMap.Builder() + .set(ServicePaths.CART.getCallPath(), cartSpec) + .set(ServicePaths.ORDER.getCallPath(), orderSpec) + .build(); + gson = new GsonBuilder() + .setVersion(CURRENT_VERSION) + .registerTypeAdapter(ValueBasedId.class, new ValueBasedId.GsonTypeAdapter()) + .registerTypeAdapter(MetaData.class, new MetaData.GsonTypeAdapter()) + .create(); + Repository, Cart> carts = + new RepositoryInMemory, Cart>(ValueBasedId.class, Cart.class); + Repository, Order> orders = + new RepositoryInMemory, Order>(ValueBasedId.class, Order.class); + responseBuilders = new ResponseBuilderMap.Builder() + .set(cartSpec.getResourceType(), new RestResponseBuilder, Cart>(carts)) + .set(orderSpec.getResourceType(), new RestResponseBuilder, Order>(orders)) + .build(); + } + + public IDFactory> getIDFactory(RestCallSpec callSpec) { + return new IDFactory>(ValueBasedId.class, callSpec.getResourceType()); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public RestRequest getRestRequest(Gson gson, RestCallSpec callSpec, CallPath callPath, + HttpServletRequest request, IDFactory> idFactory) { + RestRequestReceiver requestReceiver = new RestRequestReceiver(gson, callSpec.getRequestSpec()); + return requestReceiver.receive(request, idFactory.createId(callPath.getResourceId())); + } + + public void service(HttpServletRequest req, HttpServletResponse res, CallPath callPath) { + RestCallSpec callSpec = resourceMap.get(callPath).createCopy(callPath); + @SuppressWarnings("rawtypes") + RestRequestReceiver requestReceiver = new RestRequestReceiver(gson, callSpec.getRequestSpec()); + IDFactory> idFactory = getIDFactory(callSpec); + RestRequest restRequest = getRestRequest(gson, callSpec, callPath, req, idFactory); + RestResponse.Builder response = new RestResponse.Builder(callSpec.getResponseSpec()); + RestResponseBuilder responseBuilder = responseBuilders.get(callSpec.getResourceType()); + responseBuilder.buildResponse(callSpec, restRequest, response); + RestResponse restResponse = response.build(); + RestResponseSender responseSender = new RestResponseSender(gson); + responseSender.send(res, restResponse); + } +} diff --git a/wsf/src/main/java/com/google/gson/rest/server/ResponseBuilderMap.java b/wsf/src/main/java/com/google/gson/rest/server/ResponseBuilderMap.java new file mode 100644 index 00000000..e4c6b86a --- /dev/null +++ b/wsf/src/main/java/com/google/gson/rest/server/ResponseBuilderMap.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.gson.rest.server; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; + +import com.google.gson.rest.definition.ID; +import com.google.gson.rest.definition.RestCallSpec; +import com.google.gson.rest.definition.RestResource; + +/** + * A map of {@link RestCallSpec}, {@link RestResponseBuilder} to help figure out which + * {@link RestResponseBuilder} to use for a {@link RestCallSpec}. + * + * @author Inderjeet Singh + */ +public final class ResponseBuilderMap { + public static final class Builder { + private final Map> map = + new HashMap>(); + + public > Builder set( + Type resourceType, RestResponseBuilder responseBuilder) { + map.put(resourceType, responseBuilder); + return this; + } + + public ResponseBuilderMap build() { + return new ResponseBuilderMap(map); + } + } + + private final Map> map; + + public ResponseBuilderMap(Map> map) { + this.map = map; + } + + public RestResponseBuilder get(Type resourceType) { + return (RestResponseBuilder)map.get(resourceType); + } +} diff --git a/wsf/src/main/java/com/google/gson/rest/server/RestResponseBuilder.java b/wsf/src/main/java/com/google/gson/rest/server/RestResponseBuilder.java index 6536f5c8..9902fd89 100644 --- a/wsf/src/main/java/com/google/gson/rest/server/RestResponseBuilder.java +++ b/wsf/src/main/java/com/google/gson/rest/server/RestResponseBuilder.java @@ -22,7 +22,7 @@ import com.google.gson.rest.definition.RestResource; import com.google.gson.rest.definition.RestResponse; import com.google.gson.webservice.definition.HttpMethod; -public abstract class RestResponseBuilder> { +public class RestResponseBuilder> { protected final Repository resources; public RestResponseBuilder(Repository resources) {