From 5adfa4bc04bbec81e1b56580d96b95bcc0808b3d Mon Sep 17 00:00:00 2001 From: Inderjeet Singh Date: Sun, 17 Oct 2010 16:01:36 +0000 Subject: [PATCH] Defined an interface RestResource that all resources need to extend from. Defined an Id class for a resource. Changed a rest body spec to take a type instead of a class. This allows parameterized resource types. Defined a repository interface with an in-memory implementation for server-side storage of rest resource instances. --- .../gson/webservice/client/RestClient.java | 14 +-- .../webservice/client/RestRequestSender.java | 5 +- .../client/RestResponseReceiver.java | 21 ++-- .../webservice/definition/rest/HasId.java | 29 ++++++ .../gson/webservice/definition/rest/Id.java | 75 +++++++++++++++ .../definition/rest/ResourceMap.java | 17 ++-- .../webservice/definition/rest/RestCall.java | 8 +- .../definition/rest/RestCallSpec.java | 48 ++++++---- .../definition/rest/RestRequest.java | 16 +++- .../definition/rest/RestRequestSpec.java | 16 ++-- .../definition/rest/RestResource.java | 27 ++++++ .../definition/rest/RestResponse.java | 32 +++---- .../definition/rest/RestResponseSpec.java | 16 ++-- wsf/pom.xml | 6 -- .../google/gson/wsf/server/rest/IdMap.java | 77 +++++++++++++++ .../google/gson/wsf/server/rest/MetaData.java | 38 ++++++++ .../gson/wsf/server/rest/Repository.java | 48 ++++++++++ .../wsf/server/rest/RepositoryInMemory.java | 95 +++++++++++++++++++ .../wsf/server/rest/RestRequestReceiver.java | 12 +-- .../wsf/server/rest/RestResponseBuilder.java | 53 ++++++++++- .../wsf/server/rest/RestResponseSender.java | 3 +- 21 files changed, 553 insertions(+), 103 deletions(-) create mode 100644 wsdef/src/main/java/com/google/gson/webservice/definition/rest/HasId.java create mode 100644 wsdef/src/main/java/com/google/gson/webservice/definition/rest/Id.java create mode 100644 wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestResource.java create mode 100644 wsf/src/main/java/com/google/gson/wsf/server/rest/IdMap.java create mode 100644 wsf/src/main/java/com/google/gson/wsf/server/rest/MetaData.java create mode 100644 wsf/src/main/java/com/google/gson/wsf/server/rest/Repository.java create mode 100644 wsf/src/main/java/com/google/gson/wsf/server/rest/RepositoryInMemory.java diff --git a/wsclient/src/main/java/com/google/gson/webservice/client/RestClient.java b/wsclient/src/main/java/com/google/gson/webservice/client/RestClient.java index a6567b21..54a8d9a3 100644 --- a/wsclient/src/main/java/com/google/gson/webservice/client/RestClient.java +++ b/wsclient/src/main/java/com/google/gson/webservice/client/RestClient.java @@ -27,6 +27,7 @@ import com.google.gson.GsonBuilder; import com.google.gson.webservice.definition.WebServiceSystemException; import com.google.gson.webservice.definition.rest.RestCallSpec; import com.google.gson.webservice.definition.rest.RestRequest; +import com.google.gson.webservice.definition.rest.RestResource; import com.google.gson.webservice.definition.rest.RestResponse; /** @@ -49,7 +50,7 @@ public class RestClient { this.logLevel = logLevel; } - private URL getWebServiceUrl(RestCallSpec callSpec) { + private URL getWebServiceUrl(RestCallSpec callSpec) { String url = config.getServiceBaseUrl() + callSpec.getPath().get(); try { return new URL(url); @@ -58,13 +59,14 @@ public class RestClient { } } - public RestResponse getResponse(RestCallSpec callSpec, RestRequest request) { + public > RestResponse getResponse( + RestCallSpec callSpec, RestRequest request) { Gson gson = new GsonBuilder().create(); return getResponse(callSpec, request, gson); } - public RestResponse getResponse( - RestCallSpec callSpec, RestRequest request, Gson gson) { + public > RestResponse getResponse( + RestCallSpec callSpec, RestRequest request, Gson gson) { HttpURLConnection conn = null; try { URL webServiceUrl = getWebServiceUrl(callSpec); @@ -84,8 +86,8 @@ public class RestClient { * Use this method if you want to mange the HTTP Connection yourself. This is useful when you * want to use HTTP pipelining. */ - public RestResponse getResponse( - RestCallSpec callSpec, RestRequest request, Gson gson, HttpURLConnection conn) { + public > RestResponse getResponse( + RestCallSpec callSpec, RestRequest request, Gson gson, HttpURLConnection conn) { try { if (logger != null) { URL webServiceUrl = getWebServiceUrl(callSpec); diff --git a/wsclient/src/main/java/com/google/gson/webservice/client/RestRequestSender.java b/wsclient/src/main/java/com/google/gson/webservice/client/RestRequestSender.java index 6c432691..3a90272f 100644 --- a/wsclient/src/main/java/com/google/gson/webservice/client/RestRequestSender.java +++ b/wsclient/src/main/java/com/google/gson/webservice/client/RestRequestSender.java @@ -27,6 +27,7 @@ import com.google.gson.webservice.definition.HeaderMap; import com.google.gson.webservice.definition.HeaderMapSpec; import com.google.gson.webservice.definition.WebServiceSystemException; import com.google.gson.webservice.definition.rest.RestRequest; +import com.google.gson.webservice.definition.rest.RestResource; /** * Class to send Web service requests on a {@link HttpURLConnection}. @@ -48,7 +49,7 @@ public final class RestRequestSender { this.logLevel = logLevel; } - public void send(HttpURLConnection conn, RestRequest request) { + public > void send(HttpURLConnection conn, RestRequest request) { try { conn.setRequestMethod(request.getHttpMethod().toString()); setHeader(conn, "Content-Type", request.getContentType(), true); @@ -63,7 +64,7 @@ public final class RestRequestSender { // Android Java VM ignore Content-Length if setDoOutput is not set conn.setDoOutput(true); if (requestBody != null) { - requestBodyContents = gson.toJson(requestBody); + requestBodyContents = gson.toJson(requestBody, request.getSpec().getResourceType()); } String contentLength = String.valueOf(requestBodyContents.length()); setHeader(conn, "Content-Length", contentLength, true); diff --git a/wsclient/src/main/java/com/google/gson/webservice/client/RestResponseReceiver.java b/wsclient/src/main/java/com/google/gson/webservice/client/RestResponseReceiver.java index f2d2a4cf..a178800e 100644 --- a/wsclient/src/main/java/com/google/gson/webservice/client/RestResponseReceiver.java +++ b/wsclient/src/main/java/com/google/gson/webservice/client/RestResponseReceiver.java @@ -30,6 +30,7 @@ import com.google.gson.webservice.definition.ContentBodySpec; import com.google.gson.webservice.definition.HeaderMap; import com.google.gson.webservice.definition.HeaderMapSpec; import com.google.gson.webservice.definition.WebServiceSystemException; +import com.google.gson.webservice.definition.rest.RestResource; import com.google.gson.webservice.definition.rest.RestResponse; import com.google.gson.webservice.definition.rest.RestResponseSpec; @@ -38,16 +39,16 @@ import com.google.gson.webservice.definition.rest.RestResponseSpec; * * @author inder */ -public final class RestResponseReceiver { +public final class RestResponseReceiver> { private final Gson gson; - private final RestResponseSpec spec; + private final RestResponseSpec spec; private final Logger logger; private final Level logLevel; - public RestResponseReceiver(Gson gson, RestResponseSpec spec) { + public RestResponseReceiver(Gson gson, RestResponseSpec spec) { this(gson, spec, null); } - public RestResponseReceiver(Gson gson, RestResponseSpec spec, Level logLevel) { + public RestResponseReceiver(Gson gson, RestResponseSpec spec, Level logLevel) { this.gson = gson; this.spec = spec; this.logger = logLevel == null ? null : Logger.getLogger(RestResponseReceiver.class.getName()); @@ -57,11 +58,11 @@ public final class RestResponseReceiver { public RestResponse receive(HttpURLConnection conn) { try { HeaderMapSpec paramSpec = spec.getHeadersSpec(); - Class bodySpec = spec.getResourceClass(); + Type bodyType = spec.getResourceType(); // read response HeaderMap responseParams = readResponseHeaders(conn, paramSpec); - R responseBody = readResponseBody(conn, bodySpec); - return new RestResponse(responseParams, responseBody); + R responseBody = readResponseBody(conn, bodyType); + return new RestResponse(responseParams, responseBody, bodyType); } catch (IOException e) { throw new WebServiceSystemException(e); } @@ -84,11 +85,13 @@ public final class RestResponseReceiver { return paramsBuilder.build(); } - private R readResponseBody(HttpURLConnection conn, Class resourceClass) throws IOException { + @SuppressWarnings("unchecked") + private R readResponseBody( + HttpURLConnection conn, Type resourceType) throws IOException { String connContentType = conn.getContentType(); Preconditions.checkArgument(connContentType.contains(ContentBodySpec.JSON_CONTENT_TYPE), conn); Reader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); - R body = gson.fromJson(reader, resourceClass); + R body = (R) gson.fromJson(reader, resourceType); return body; } } diff --git a/wsdef/src/main/java/com/google/gson/webservice/definition/rest/HasId.java b/wsdef/src/main/java/com/google/gson/webservice/definition/rest/HasId.java new file mode 100644 index 00000000..3bd9ad42 --- /dev/null +++ b/wsdef/src/main/java/com/google/gson/webservice/definition/rest/HasId.java @@ -0,0 +1,29 @@ +/* + * 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.webservice.definition.rest; + +/** + * An interface to indicate that an object has an Id + * + * @author inder + * + * @param type of object + */ +public interface HasId { + public Id getId(); + public void setId(Id id); + public boolean hasId(); +} diff --git a/wsdef/src/main/java/com/google/gson/webservice/definition/rest/Id.java b/wsdef/src/main/java/com/google/gson/webservice/definition/rest/Id.java new file mode 100644 index 00000000..20b3349d --- /dev/null +++ b/wsdef/src/main/java/com/google/gson/webservice/definition/rest/Id.java @@ -0,0 +1,75 @@ +/* + * 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.webservice.definition.rest; + +import java.lang.reflect.Type; + +/** + * An id for a rest resource + * + * @author inder + * + * @param type variable for the rest resource + */ +public final class Id { + private final long value; + private final Type typeOfId; + + private Id(long value, Type typeOfId) { + this.value = value; + this.typeOfId = typeOfId; + } + + public long getValue() { + return value; + } + + public Type getTypeOfId() { + return typeOfId; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((typeOfId == null) ? 0 : typeOfId.hashCode()); + result = prime * result + (int)(value ^ (value >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + @SuppressWarnings("unchecked") + Id other = (Id)obj; + if (typeOfId == null) { + if (other.typeOfId != null) return false; + } else if (!typeOfId.equals(other.typeOfId)) return false; + if (value != other.value) return false; + return true; + } + + public static Id get(long value, Type typeOfId) { + return new Id(value, typeOfId); + } + + @Override + public String toString() { + return String.format("{value:%s,type:%s}", value, typeOfId); + } +} diff --git a/wsdef/src/main/java/com/google/gson/webservice/definition/rest/ResourceMap.java b/wsdef/src/main/java/com/google/gson/webservice/definition/rest/ResourceMap.java index 920eb615..db2fcd4e 100644 --- a/wsdef/src/main/java/com/google/gson/webservice/definition/rest/ResourceMap.java +++ b/wsdef/src/main/java/com/google/gson/webservice/definition/rest/ResourceMap.java @@ -28,10 +28,11 @@ import com.google.gson.webservice.definition.CallPath; public final class ResourceMap { public static final class Builder { - private final Map> resources = - new HashMap>(); + private final Map resources = + new HashMap(); - public Builder set(CallPath callPath, RestCallSpec spec) { + public Builder set(CallPath callPath, RestCallSpec spec) { + Preconditions.checkArgument(resources.get(callPath) == null); resources.put(callPath, spec); return this; } @@ -41,15 +42,13 @@ public final class ResourceMap { } } - private final Map> resources; + private final Map resources; - public ResourceMap(Map> resources) { + public ResourceMap(Map resources) { this.resources = resources; } - public RestCallSpec get(CallPath callPath) { - @SuppressWarnings("unchecked") - RestCallSpec restCallSpec = (RestCallSpec)resources.get(callPath); - return restCallSpec; + public RestCallSpec get(CallPath callPath) { + return (RestCallSpec)resources.get(callPath); } } diff --git a/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestCall.java b/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestCall.java index 9af43f5c..6d6b0acd 100644 --- a/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestCall.java +++ b/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestCall.java @@ -21,19 +21,19 @@ package com.google.gson.webservice.definition.rest; * * @author inder */ -public final class RestCall { +public final class RestCall> { - private final RestCallSpec callSpec; + private final RestCallSpec callSpec; private final RestRequest request; private final RestResponse response; - public RestCall(RestCallSpec callSpec, RestRequest request, RestResponse response) { + public RestCall(RestCallSpec callSpec, RestRequest request, RestResponse response) { this.callSpec = callSpec; this.request = request; this.response = response; } - public RestCallSpec getSpec() { + public RestCallSpec getSpec() { return callSpec; } diff --git a/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestCallSpec.java b/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestCallSpec.java index 1d567ada..c7163459 100644 --- a/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestCallSpec.java +++ b/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestCallSpec.java @@ -15,6 +15,7 @@ */ package com.google.gson.webservice.definition.rest; +import java.lang.reflect.Type; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.Set; @@ -29,61 +30,64 @@ import com.google.gson.webservice.definition.TypedKey; * * @author inder */ -public final class RestCallSpec { - public static class Builder { +public final class RestCallSpec { + public static class Builder { private final CallPath callPath; private final Set supportedHttpMethods = new LinkedHashSet(); private final HeaderMapSpec.Builder reqParamsSpecBuilder = new HeaderMapSpec.Builder(); private final HeaderMapSpec.Builder resParamsSpecBuilder = new HeaderMapSpec.Builder(); - private final Class resourceClass; + private final Type resourceType; - public Builder(CallPath callPath, Class resourceClass) { + public Builder(CallPath callPath, Type resourceType) { this.callPath = callPath; supportedHttpMethods.addAll(HttpMethod.ALL_METHODS); - this.resourceClass = resourceClass; + this.resourceType = resourceType; } - public Builder disableHttpMethod(HttpMethod httpMethod) { + public Builder disableHttpMethod(HttpMethod httpMethod) { supportedHttpMethods.remove(httpMethod); return this; } - public Builder addRequestParam(TypedKey param) { + public Builder addRequestParam(TypedKey param) { reqParamsSpecBuilder.put(param.getName(), param.getClassOfT()); return this; } - public Builder addResponseParam(TypedKey param) { + public Builder addResponseParam(TypedKey param) { resParamsSpecBuilder.put(param.getName(), param.getClassOfT()); return this; } - public RestCallSpec build() { + public RestCallSpec build() { if (supportedHttpMethods.isEmpty()) { supportedHttpMethods.addAll(Arrays.asList(HttpMethod.values())); } - RestRequestSpec requestSpec = - new RestRequestSpec(reqParamsSpecBuilder.build(), resourceClass); - RestResponseSpec responseSpec = - new RestResponseSpec(resParamsSpecBuilder.build(), resourceClass); - return new RestCallSpec(supportedHttpMethods, callPath, - requestSpec, responseSpec); + RestRequestSpec requestSpec = + new RestRequestSpec(reqParamsSpecBuilder.build(), resourceType); + RestResponseSpec responseSpec = + new RestResponseSpec(resParamsSpecBuilder.build(), resourceType); + return new RestCallSpec(supportedHttpMethods, callPath, + requestSpec, responseSpec, resourceType); } } private final Set supportedHttpMethods; private final CallPath path; - private final RestRequestSpec requestSpec; - private final RestResponseSpec responseSpec; + private final RestRequestSpec requestSpec; + private final RestResponseSpec responseSpec; + private final Type resourceType; private RestCallSpec(Set supportedHttpMethods, CallPath path, - RestRequestSpec requestSpec, RestResponseSpec responseSpec) { + RestRequestSpec requestSpec, RestResponseSpec responseSpec, + Type resourceType) { Preconditions.checkArgument(!supportedHttpMethods.isEmpty()); Preconditions.checkNotNull(path); this.supportedHttpMethods = supportedHttpMethods; this.path = path; this.requestSpec = requestSpec; this.responseSpec = responseSpec; + this.resourceType = resourceType; } public CallPath getPath() { @@ -94,11 +98,15 @@ public final class RestCallSpec { return supportedHttpMethods; } - public RestResponseSpec getResponseSpec() { + public RestResponseSpec getResponseSpec() { return responseSpec; } - public RestRequestSpec getRequestSpec() { + public RestRequestSpec getRequestSpec() { return requestSpec; } + + public Type getResourceType() { + return resourceType; + } } diff --git a/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestRequest.java b/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestRequest.java index d354b88a..41a39a25 100644 --- a/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestRequest.java +++ b/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestRequest.java @@ -15,6 +15,8 @@ */ package com.google.gson.webservice.definition.rest; +import java.lang.reflect.Type; + import com.google.gson.webservice.definition.ContentBodySpec; import com.google.gson.webservice.definition.HeaderMap; import com.google.gson.webservice.definition.HttpMethod; @@ -27,25 +29,29 @@ import com.google.gson.webservice.definition.TypedKey; * * @author inder */ -public final class RestRequest { +public final class RestRequest> { private final HttpMethod method; private final HeaderMap headers; private final R body; - private final RestRequestSpec spec; + private final RestRequestSpec spec; public RestRequest(HttpMethod method, HeaderMap requestHeaders, - R requestBody, Class resourceClass) { + R requestBody, Type resourceType) { this.method = method; this.body = requestBody; this.headers = requestHeaders; - this.spec = new RestRequestSpec(requestHeaders.getSpec(), resourceClass); + this.spec = new RestRequestSpec(requestHeaders.getSpec(), resourceType); + } + + public Id getId() { + return body.getId(); } public HttpMethod getMethod() { return method; } - public RestRequestSpec getSpec() { + public RestRequestSpec getSpec() { return spec; } diff --git a/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestRequestSpec.java b/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestRequestSpec.java index 4595fb36..9fba1053 100644 --- a/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestRequestSpec.java +++ b/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestRequestSpec.java @@ -15,6 +15,8 @@ */ package com.google.gson.webservice.definition.rest; +import java.lang.reflect.Type; + import com.google.gson.webservice.definition.HeaderMapSpec; /** @@ -22,17 +24,17 @@ import com.google.gson.webservice.definition.HeaderMapSpec; * * @author inder */ -public final class RestRequestSpec { +public final class RestRequestSpec { private final HeaderMapSpec headersSpec; - private final Class resourceClass; + private final Type resourceType; - public RestRequestSpec(HeaderMapSpec headersSpec, Class resourceClass) { + public RestRequestSpec(HeaderMapSpec headersSpec, Type resourceClass) { this.headersSpec = headersSpec; - this.resourceClass = resourceClass; + this.resourceType = resourceClass; } - public Class getResourceClass() { - return resourceClass; + public Type getResourceType() { + return resourceType; } public HeaderMapSpec getHeadersSpec() { @@ -41,6 +43,6 @@ public final class RestRequestSpec { @Override public String toString() { - return String.format("{headersSpec:%s,resourceClass:%s}", headersSpec, resourceClass); + return String.format("{headersSpec:%s,resourceType:%s}", headersSpec, resourceType); } } diff --git a/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestResource.java b/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestResource.java new file mode 100644 index 00000000..ed414095 --- /dev/null +++ b/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestResource.java @@ -0,0 +1,27 @@ +/* + * 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.webservice.definition.rest; + +/** + * An interface implemented by an object that is intended to be a rest resource + * + * @author inder + * + * @param the rest resource type + */ +public interface RestResource extends HasId { + +} diff --git a/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestResponse.java b/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestResponse.java index e32829b8..e5d92b73 100644 --- a/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestResponse.java +++ b/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestResponse.java @@ -15,6 +15,8 @@ */ package com.google.gson.webservice.definition.rest; +import java.lang.reflect.Type; + import com.google.gson.webservice.definition.HeaderMap; import com.google.gson.webservice.definition.TypedKey; @@ -24,52 +26,50 @@ import com.google.gson.webservice.definition.TypedKey; * * @author inder */ -public final class RestResponse { +public final class RestResponse> { private final HeaderMap headers; private final R body; - private final RestResponseSpec spec; + private final RestResponseSpec spec; - public static class Builder { + public static class Builder> { private final HeaderMap.Builder headers; - private R body; - private final RestResponseSpec spec; + private RS body; + private final RestResponseSpec spec; - public Builder(RestResponseSpec spec) { + public Builder(RestResponseSpec spec) { this.spec = spec; headers = new HeaderMap.Builder(spec.getHeadersSpec()); } - public Builder putHeader(TypedKey paramName, T content) { + public Builder putHeader(TypedKey paramName, T content) { headers.put(paramName.getName(), content, paramName.getClassOfT()); return this; } - public Builder setBody(R body) { + public Builder setBody(RS body) { this.body = body; return this; } - public RestResponse build() { - return new RestResponse(spec, headers.build(), body); + public RestResponse build() { + return new RestResponse(spec, headers.build(), body); } } - private RestResponse(RestResponseSpec spec, HeaderMap headers, R body) { + private RestResponse(RestResponseSpec spec, HeaderMap headers, R body) { this.spec = spec; this.headers = headers; this.body = body; } - @SuppressWarnings("unchecked") - public RestResponse(HeaderMap responseHeaders, R responseBody) { - this.spec = new RestResponseSpec(responseHeaders.getSpec(), - (Class)responseBody.getClass()); + public RestResponse(HeaderMap responseHeaders, R responseBody, Type responseBodyType) { + this.spec = new RestResponseSpec(responseHeaders.getSpec(), responseBodyType); this.headers = responseHeaders; this.body = responseBody; } - public RestResponseSpec getSpec() { + public RestResponseSpec getSpec() { return spec; } diff --git a/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestResponseSpec.java b/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestResponseSpec.java index 679bc3fd..2ce01aea 100644 --- a/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestResponseSpec.java +++ b/wsdef/src/main/java/com/google/gson/webservice/definition/rest/RestResponseSpec.java @@ -15,6 +15,8 @@ */ package com.google.gson.webservice.definition.rest; +import java.lang.reflect.Type; + import com.google.gson.webservice.definition.HeaderMapSpec; /** @@ -22,17 +24,17 @@ import com.google.gson.webservice.definition.HeaderMapSpec; * * @author inder */ -public final class RestResponseSpec { +public final class RestResponseSpec { private final HeaderMapSpec headersSpec; - private final Class resourceClass; + private final Type resourceType; - public RestResponseSpec(HeaderMapSpec headersSpec, Class resourceClass) { + public RestResponseSpec(HeaderMapSpec headersSpec, Type resourceType) { this.headersSpec = headersSpec; - this.resourceClass = resourceClass; + this.resourceType = resourceType; } - public Class getResourceClass() { - return resourceClass; + public Type getResourceType() { + return resourceType; } public HeaderMapSpec getHeadersSpec() { @@ -41,6 +43,6 @@ public final class RestResponseSpec { @Override public String toString() { - return String.format("{headersSpec:%s,resourceClass:%s}", headersSpec, resourceClass); + return String.format("{headersSpec:%s,resourceType:%s}", headersSpec, resourceType); } } diff --git a/wsf/pom.xml b/wsf/pom.xml index 2fac729c..31c67b7d 100755 --- a/wsf/pom.xml +++ b/wsf/pom.xml @@ -69,12 +69,6 @@ 0.1 compile - - com.google.code.gson - wsclient - 0.1 - compile - diff --git a/wsf/src/main/java/com/google/gson/wsf/server/rest/IdMap.java b/wsf/src/main/java/com/google/gson/wsf/server/rest/IdMap.java new file mode 100644 index 00000000..d04f61ae --- /dev/null +++ b/wsf/src/main/java/com/google/gson/wsf/server/rest/IdMap.java @@ -0,0 +1,77 @@ +/* + * 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.wsf.server.rest; + +import java.lang.reflect.Type; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.google.gson.webservice.definition.rest.HasId; +import com.google.gson.webservice.definition.rest.Id; + +/** + * This class provides a type-safe map to access values associated with Ids + * + * @author inder + * + * @param the type of the objects being kept in the map + */ +public class IdMap> { + public static final Logger LOG = Logger.getLogger(IdMap.class.getName()); + protected final Map, T> map; + private volatile long nextAvailableId; + private final Type typeOfId; + + /** + * Use {@link #create(Class)} instead of constructor + */ + protected IdMap(Type typeOfId) { + this.typeOfId = typeOfId; + map = new ConcurrentHashMap, T>(); + nextAvailableId = 0; + } + + public T get(Id id) { + return map.get(id); + } + + public T put(T obj) { + map.put(obj.getId(), obj); + return obj; + } + + public void delete(Id id) { + T removed = map.remove(id); + if (removed == null) { + LOG.log(Level.WARNING, "Attempted to delete non-existent id: {0}", id); + } + } + + public boolean exists(Id id) { + return map.containsKey(id); + } + + public synchronized Id getNextId() { + long id = nextAvailableId++; + return Id.get(id, typeOfId); + } + + public static > IdMap create(Type typeOfId) { + return new IdMap(typeOfId); + } +} diff --git a/wsf/src/main/java/com/google/gson/wsf/server/rest/MetaData.java b/wsf/src/main/java/com/google/gson/wsf/server/rest/MetaData.java new file mode 100644 index 00000000..4b23a0f8 --- /dev/null +++ b/wsf/src/main/java/com/google/gson/wsf/server/rest/MetaData.java @@ -0,0 +1,38 @@ +/* + * 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.wsf.server.rest; + +import com.google.gson.webservice.definition.rest.RestResource; + +/** + * Metadata associated with a repository for a rest resource + * + * @author inder + * + * @param The resource + */ +public final class MetaData> { + + final boolean freshlyAssignedId; + + public MetaData(boolean freshlyAssignedId) { + this.freshlyAssignedId = freshlyAssignedId; + } + + public boolean isFreshlyAssignedId() { + return freshlyAssignedId; + } +} \ No newline at end of file diff --git a/wsf/src/main/java/com/google/gson/wsf/server/rest/Repository.java b/wsf/src/main/java/com/google/gson/wsf/server/rest/Repository.java new file mode 100644 index 00000000..15951fcd --- /dev/null +++ b/wsf/src/main/java/com/google/gson/wsf/server/rest/Repository.java @@ -0,0 +1,48 @@ +/* + * 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.wsf.server.rest; + +import com.google.gson.webservice.definition.rest.HasId; +import com.google.gson.webservice.definition.rest.Id; + +/** + * An interface for a repository of rest resources. Meant for abstracting the server-side + * storage of rest resources. + * + * @author inder + * + * @param the type of rest resource + */ +public interface Repository> { + public R get(Id resourceId); + + /** + * if resource.getId() == null, inserts the resource after assigning it a new id. + * Otherwise, updates the resource ensuring that it pre-exists. + */ + public R put(R resource); + + public void delete(Id resourceId); + + public boolean exists(Id resourceId); + + /** + * Ensures that the specified resource has a valid id that will be used when it is saved + */ + public Id assignId(R resource); + + public Id getNextId(); +} diff --git a/wsf/src/main/java/com/google/gson/wsf/server/rest/RepositoryInMemory.java b/wsf/src/main/java/com/google/gson/wsf/server/rest/RepositoryInMemory.java new file mode 100644 index 00000000..9ccd7156 --- /dev/null +++ b/wsf/src/main/java/com/google/gson/wsf/server/rest/RepositoryInMemory.java @@ -0,0 +1,95 @@ +/* + * 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.wsf.server.rest; + +import java.util.Map; + +import com.google.gson.webservice.definition.rest.Id; +import com.google.gson.webservice.definition.rest.RestResource; +import com.google.inject.internal.Maps; +import com.google.inject.internal.Preconditions; + +/** + * An in-memory map of rest resources + * + * @author inder + * + * @param Type variable for the resource + */ +public class RepositoryInMemory> implements Repository { + private final IdMap resources; + private final Map, MetaData> metaDataMap; + + public RepositoryInMemory(Class classOfResource) { + this.resources = IdMap.create(classOfResource); + this.metaDataMap = Maps.newHashMap(); + } + + @Override + public R get(Id resourceId) { + return resources.get(resourceId); + } + + public boolean isFreshlyAssignedId(Id resourceId) { + MetaData metaData = metaDataMap.get(resourceId); + if (metaData == null) { + return false; + } + return metaData.isFreshlyAssignedId(); + } + + @Override + public R put(R resource) { + if (!resource.hasId()) { + // insert semantics + assignId(resource); + } else { + Id id = resource.getId(); + if (!isFreshlyAssignedId(id)) { + // update semantics + Preconditions.checkState(resources.exists(resource.getId())); + } + } + resource = resources.put(resource); + metaDataMap.remove(resource.getId()); + return resource; + } + + @Override + public void delete(Id resourceId) { + resources.delete(resourceId); + } + + @Override + public boolean exists(Id resourceId) { + return resources.exists(resourceId); + } + + @Override + public Id getNextId() { + return resources.getNextId(); + } + + @Override + public Id assignId(R resource) { + if (resource.getId() == null) { + Id id = resources.getNextId(); + resource.setId(id); + metaDataMap.put(id, new MetaData(true)); + } + return resource.getId(); + } +} diff --git a/wsf/src/main/java/com/google/gson/wsf/server/rest/RestRequestReceiver.java b/wsf/src/main/java/com/google/gson/wsf/server/rest/RestRequestReceiver.java index 196ef5da..d9adcf89 100644 --- a/wsf/src/main/java/com/google/gson/wsf/server/rest/RestRequestReceiver.java +++ b/wsf/src/main/java/com/google/gson/wsf/server/rest/RestRequestReceiver.java @@ -32,18 +32,19 @@ import com.google.gson.webservice.definition.HttpMethod; import com.google.gson.webservice.definition.WebServiceSystemException; import com.google.gson.webservice.definition.rest.RestRequest; import com.google.gson.webservice.definition.rest.RestRequestSpec; +import com.google.gson.webservice.definition.rest.RestResource; /** * Receives and parses a request at the server side on a {@link HttpServletRequest}. * * @author inder */ -public final class RestRequestReceiver { +public final class RestRequestReceiver> { private final Gson gson; - private final RestRequestSpec spec; + private final RestRequestSpec spec; - public RestRequestReceiver(Gson gson, RestRequestSpec spec) { + public RestRequestReceiver(Gson gson, RestRequestSpec spec) { this.gson = gson; this.spec = spec; } @@ -54,7 +55,7 @@ public final class RestRequestReceiver { R requestBody = buildRequestBody(request); HttpMethod method = HttpMethod.getMethod(request.getMethod()); - return new RestRequest(method, requestParams, requestBody, spec.getResourceClass()); + return new RestRequest(method, requestParams, requestBody, spec.getResourceType()); } catch (IOException e) { throw new WebServiceSystemException(e); } catch (JsonParseException e) { @@ -83,9 +84,8 @@ public final class RestRequestReceiver { } private R buildRequestBody(HttpServletRequest request) throws IOException { - Class resourceClass = spec.getResourceClass(); Reader reader = new BufferedReader(new InputStreamReader(request.getInputStream())); - R requestBody = gson.fromJson(reader, resourceClass); + R requestBody = gson.fromJson(reader, spec.getResourceType()); return requestBody; } } diff --git a/wsf/src/main/java/com/google/gson/wsf/server/rest/RestResponseBuilder.java b/wsf/src/main/java/com/google/gson/wsf/server/rest/RestResponseBuilder.java index 65aeb9bd..c8a41306 100644 --- a/wsf/src/main/java/com/google/gson/wsf/server/rest/RestResponseBuilder.java +++ b/wsf/src/main/java/com/google/gson/wsf/server/rest/RestResponseBuilder.java @@ -15,11 +15,54 @@ */ package com.google.gson.wsf.server.rest; -import com.google.gson.webservice.definition.rest.RestRequest; -import com.google.gson.webservice.definition.rest.RestResponse; +import com.google.gson.webservice.definition.HttpMethod; +import com.google.gson.webservice.definition.rest.Id; import com.google.gson.webservice.definition.rest.RestCallSpec; +import com.google.gson.webservice.definition.rest.RestRequest; +import com.google.gson.webservice.definition.rest.RestResource; +import com.google.gson.webservice.definition.rest.RestResponse; -public interface RestResponseBuilder { - public void buildResponse(RestCallSpec callSpec, RestRequest request, - RestResponse.Builder responseBuilder); +public abstract class RestResponseBuilder> { + protected final Repository resources; + + public RestResponseBuilder(Repository resources) { + this.resources = resources; + } + + public void buildResponse(RestCallSpec callSpec, RestRequest request, + RestResponse.Builder responseBuilder) { + HttpMethod method = request.getMethod(); + R responseBody = null; + switch (method) { + case GET: + responseBody = get(request.getId()); + break; + case POST: + responseBody = post(request.getBody()); + break; + case DELETE: + delete(request.getId()); + break; + case PUT: + default: + throw new IllegalStateException("Unexpected method: " + method); + } + responseBuilder.setBody(responseBody); + } + + public R get(Id resourceId) { + return resources.get(resourceId); + } + + public R post(R resource) { + return resources.put(resource); + } + + public void delete(Id resourceId) { + resources.delete(resourceId); + } + + public R put(R resource) { + return resources.put(resource); + } } diff --git a/wsf/src/main/java/com/google/gson/wsf/server/rest/RestResponseSender.java b/wsf/src/main/java/com/google/gson/wsf/server/rest/RestResponseSender.java index cad49138..edfa5f31 100644 --- a/wsf/src/main/java/com/google/gson/wsf/server/rest/RestResponseSender.java +++ b/wsf/src/main/java/com/google/gson/wsf/server/rest/RestResponseSender.java @@ -26,6 +26,7 @@ import com.google.gson.Gson; import com.google.gson.webservice.definition.ContentBodySpec; import com.google.gson.webservice.definition.HeaderMap; import com.google.gson.webservice.definition.HeaderMapSpec; +import com.google.gson.webservice.definition.rest.RestResource; import com.google.gson.webservice.definition.rest.RestResponse; /** @@ -33,7 +34,7 @@ import com.google.gson.webservice.definition.rest.RestResponse; * * @author inder */ -public final class RestResponseSender { +public final class RestResponseSender> { private static final Logger logger = Logger.getLogger(RestResponseSender.class.getCanonicalName()); private Gson gson;