From be05420c6b6fe150faaba4152a2846fbab95408e Mon Sep 17 00:00:00 2001 From: Inderjeet Singh Date: Tue, 9 Nov 2010 18:00:08 +0000 Subject: [PATCH] Renamed Id to ValueBasedId renamed RestClient to ResourceDepotClient. Added ability to simulate an HTTP PUT or GET with a POST. Added support for extracting resource Id in callpath. Added Id to RestRequest. --- ...stClient.java => ResourceDepotClient.java} | 37 ++++++---- .../gson/rest/client/RestClientStub.java | 14 ++-- .../gson/rest/client/RestRequestSender.java | 23 ++++-- .../com/google/gson/rest/definition/ID.java | 2 +- .../gson/rest/definition/IDFactory.java | 27 ++++++- .../gson/rest/definition/ResourceDepot.java | 27 +++++++ .../gson/rest/definition/ResourceMap.java | 6 +- .../gson/rest/definition/RestCallSpec.java | 5 ++ .../gson/rest/definition/RestRequest.java | 6 +- .../definition/{Id.java => ValueBasedId.java} | 36 +++++----- .../gson/webservice/definition/CallPath.java | 72 ++++++++++++++----- .../webservice/definition/HttpMethod.java | 8 ++- .../definition/internal/utils/Pair.java | 64 +++++++++++++++++ .../google/gson/rest/definition/IdTest.java | 28 ++++---- .../rest/definition/IdTypeAdapterTest.java | 26 +++---- .../webservice/definition/CallPathTest.java | 26 +++++++ .../com/google/gson/rest/server/IdMap.java | 3 +- .../gson/rest/server/RestRequestReceiver.java | 10 ++- 18 files changed, 318 insertions(+), 102 deletions(-) rename wsclient/src/main/java/com/google/gson/rest/client/{RestClient.java => ResourceDepotClient.java} (72%) create mode 100644 wsdef/src/main/java/com/google/gson/rest/definition/ResourceDepot.java rename wsdef/src/main/java/com/google/gson/rest/definition/{Id.java => ValueBasedId.java} (81%) create mode 100644 wsdef/src/main/java/com/google/gson/webservice/definition/internal/utils/Pair.java diff --git a/wsclient/src/main/java/com/google/gson/rest/client/RestClient.java b/wsclient/src/main/java/com/google/gson/rest/client/ResourceDepotClient.java similarity index 72% rename from wsclient/src/main/java/com/google/gson/rest/client/RestClient.java rename to wsclient/src/main/java/com/google/gson/rest/client/ResourceDepotClient.java index 2db590f8..86a075fb 100644 --- a/wsclient/src/main/java/com/google/gson/rest/client/RestClient.java +++ b/wsclient/src/main/java/com/google/gson/rest/client/ResourceDepotClient.java @@ -15,7 +15,11 @@ */ package com.google.gson.rest.client; +import java.lang.reflect.Type; + +import com.google.gson.Gson; import com.google.gson.rest.definition.ID; +import com.google.gson.rest.definition.ResourceDepot; import com.google.gson.rest.definition.RestCallSpec; import com.google.gson.rest.definition.RestRequest; import com.google.gson.rest.definition.RestResource; @@ -24,26 +28,31 @@ import com.google.gson.webservice.definition.CallPath; import com.google.gson.webservice.definition.HeaderMap; import com.google.gson.webservice.definition.HttpMethod; -import java.lang.reflect.Type; - /** * A client class to access a rest resource * * @author Inderjeet Singh */ -public class RestClient> { +public class ResourceDepotClient> implements ResourceDepot { private final RestClientStub stub; private final RestCallSpec callSpec; private final Type resourceType; + private final Gson gson; - public RestClient(RestClientStub stub, CallPath callPath, Type resourceType) { - this(stub, resourceType, generateRestCallSpec(callPath, resourceType)); + /** + * @param stub stub containing server info to access the rest client + * @param callPath relative path to the resource + * @param resourceType Class for the resource. Such as Cart.class + */ + public ResourceDepotClient(RestClientStub stub, CallPath callPath, Type resourceType, Gson gson) { + this(stub, resourceType, generateRestCallSpec(callPath, resourceType), gson); } - protected RestClient(RestClientStub stub, Type resourceType, RestCallSpec callSpec) { + protected ResourceDepotClient(RestClientStub stub, Type resourceType, RestCallSpec callSpec, Gson gson) { this.stub = stub; this.callSpec = callSpec; this.resourceType = resourceType; + this.gson = gson; } private static RestCallSpec generateRestCallSpec(CallPath callPath, Type resourceType) { @@ -54,8 +63,8 @@ public class RestClient> { HeaderMap requestHeaders = new HeaderMap.Builder(callSpec.getRequestSpec().getHeadersSpec()).build(); RestRequest request = - new RestRequest(HttpMethod.GET, requestHeaders, null, resourceType); - RestResponse response = stub.getResponse(callSpec, request); + new RestRequest(HttpMethod.GET, requestHeaders, resourceId, null, resourceType); + RestResponse response = stub.getResponse(callSpec, request, gson); return response.getBody(); } @@ -63,8 +72,8 @@ public class RestClient> { HeaderMap requestHeaders = new HeaderMap.Builder(callSpec.getRequestSpec().getHeadersSpec()).build(); RestRequest request = - new RestRequest(HttpMethod.POST, requestHeaders, resource, resourceType); - RestResponse response = stub.getResponse(callSpec, request); + new RestRequest(HttpMethod.POST, requestHeaders, resource.getId(), resource, resourceType); + RestResponse response = stub.getResponse(callSpec, request, gson); return response.getBody(); } @@ -72,8 +81,8 @@ public class RestClient> { HeaderMap requestHeaders = new HeaderMap.Builder(callSpec.getRequestSpec().getHeadersSpec()).build(); RestRequest request = - new RestRequest(HttpMethod.PUT, requestHeaders, resource, resourceType); - RestResponse response = stub.getResponse(callSpec, request); + new RestRequest(HttpMethod.PUT, requestHeaders, resource.getId(), resource, resourceType); + RestResponse response = stub.getResponse(callSpec, request, gson); return response.getBody(); } @@ -81,7 +90,7 @@ public class RestClient> { HeaderMap requestHeaders = new HeaderMap.Builder(callSpec.getRequestSpec().getHeadersSpec()).build(); RestRequest request = - new RestRequest(HttpMethod.DELETE, requestHeaders, null, resourceType); - stub.getResponse(callSpec, request); + new RestRequest(HttpMethod.DELETE, requestHeaders, resourceId, null, resourceType); + stub.getResponse(callSpec, request, gson); } } diff --git a/wsclient/src/main/java/com/google/gson/rest/client/RestClientStub.java b/wsclient/src/main/java/com/google/gson/rest/client/RestClientStub.java index 1dd9c89f..4cde11e6 100644 --- a/wsclient/src/main/java/com/google/gson/rest/client/RestClientStub.java +++ b/wsclient/src/main/java/com/google/gson/rest/client/RestClientStub.java @@ -79,10 +79,7 @@ public class RestClientStub { RestCallSpec callSpec, RestRequest request, Gson gson) { HttpURLConnection conn = null; try { - URL webServiceUrl = getWebServiceUrl(callSpec, getId(request.getBody())); - if (logger != null) { - logger.log(logLevel, "Opening connection to " + webServiceUrl); - } + URL webServiceUrl = getWebServiceUrl(callSpec, request.getId()); conn = (HttpURLConnection) webServiceUrl.openConnection(); return getResponse(callSpec, request, gson, conn); } catch (IOException e) { @@ -97,10 +94,11 @@ public class RestClientStub { * want to use HTTP pipelining. */ public > RestResponse getResponse( - RestCallSpec callSpec, RestRequest request, Gson gson, HttpURLConnection conn) { + RestCallSpec callSpec, RestRequest request, Gson gson, + HttpURLConnection conn) { try { if (logger != null) { - URL webServiceUrl = getWebServiceUrl(callSpec, getId(request.getBody())); + URL webServiceUrl = getWebServiceUrl(callSpec, request.getId()); logger.log(logLevel, "Opening connection to " + webServiceUrl); } RestRequestSender requestSender = new RestRequestSender(gson, logLevel); @@ -119,10 +117,6 @@ public class RestClientStub { } } - private static > I getId(R resource) { - return (resource == null || !resource.hasId()) ? null : resource.getId(); - } - @Override public String toString() { return String.format("config:%s", config); diff --git a/wsclient/src/main/java/com/google/gson/rest/client/RestRequestSender.java b/wsclient/src/main/java/com/google/gson/rest/client/RestRequestSender.java index c0742bae..b6db8b6f 100644 --- a/wsclient/src/main/java/com/google/gson/rest/client/RestRequestSender.java +++ b/wsclient/src/main/java/com/google/gson/rest/client/RestRequestSender.java @@ -28,6 +28,7 @@ import com.google.gson.rest.definition.RestRequest; import com.google.gson.rest.definition.RestResource; import com.google.gson.webservice.definition.HeaderMap; import com.google.gson.webservice.definition.HeaderMapSpec; +import com.google.gson.webservice.definition.HttpMethod; import com.google.gson.webservice.definition.WebServiceSystemException; import com.google.gson.wsclient.internal.utils.Streams; @@ -37,6 +38,9 @@ import com.google.gson.wsclient.internal.utils.Streams; * @author inder */ public final class RestRequestSender { + private static final boolean SIMULATE_GET_WITH_POST = true; + private static final boolean SIMULATE_PUT_WITH_POST = true; + private final Gson gson; private final Logger logger; private final Level logLevel; @@ -51,9 +55,18 @@ 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()); + HttpMethod method = request.getHttpMethod(); + if (SIMULATE_PUT_WITH_POST && method == HttpMethod.PUT) { + method = HttpMethod.POST; + setHeader(conn, HttpMethod.SIMULATED_METHOD_HEADER, HttpMethod.PUT.toString(), true); + } else if (SIMULATE_GET_WITH_POST && method == HttpMethod.GET) { + method = HttpMethod.POST; + setHeader(conn, HttpMethod.SIMULATED_METHOD_HEADER, HttpMethod.GET.toString(), true); + } + conn.setRequestMethod(method.toString()); setHeader(conn, "Content-Type", request.getContentType(), true); // Assume conservatively that the response will need to be read. @@ -63,8 +76,10 @@ public final class RestRequestSender { R requestBody = request.getBody(); String requestBodyContents = ""; - // Android Java VM ignore Content-Length if setDoOutput is not set - conn.setDoOutput(true); + if (method == HttpMethod.POST || method == HttpMethod.PUT) { + // Android Java VM ignore Content-Length if setDoOutput is not set + conn.setDoOutput(true); + } if (requestBody != null) { requestBodyContents = gson.toJson(requestBody, request.getSpec().getResourceType()); } diff --git a/wsdef/src/main/java/com/google/gson/rest/definition/ID.java b/wsdef/src/main/java/com/google/gson/rest/definition/ID.java index 8a730024..55bba6e8 100644 --- a/wsdef/src/main/java/com/google/gson/rest/definition/ID.java +++ b/wsdef/src/main/java/com/google/gson/rest/definition/ID.java @@ -1,7 +1,7 @@ package com.google.gson.rest.definition; public interface ID { - public static final long INVALID_ID = 0L; + public static final long INVALID_ID = -1L; public long getValue(); } diff --git a/wsdef/src/main/java/com/google/gson/rest/definition/IDFactory.java b/wsdef/src/main/java/com/google/gson/rest/definition/IDFactory.java index 411c1c79..87d30e91 100644 --- a/wsdef/src/main/java/com/google/gson/rest/definition/IDFactory.java +++ b/wsdef/src/main/java/com/google/gson/rest/definition/IDFactory.java @@ -1,7 +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.rest.definition; import java.lang.reflect.Type; +/** + * A factory to create {@link ValueBasedId)s + * + * @author inder + * + * @param + */ public class IDFactory { private final Class classOfI; private final Type typeOfId; @@ -11,9 +33,10 @@ public class IDFactory { this.typeOfId = typeOfId; } + @SuppressWarnings("unchecked") public I createId(long value) { - if (classOfI.isAssignableFrom(Id.class)) { - return (I)Id.get(value, typeOfId); + if (classOfI.isAssignableFrom(ValueBasedId.class)) { + return (I)ValueBasedId.get(value, typeOfId); } throw new UnsupportedOperationException(); } diff --git a/wsdef/src/main/java/com/google/gson/rest/definition/ResourceDepot.java b/wsdef/src/main/java/com/google/gson/rest/definition/ResourceDepot.java new file mode 100644 index 00000000..6e045b3b --- /dev/null +++ b/wsdef/src/main/java/com/google/gson/rest/definition/ResourceDepot.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.rest.definition; + +public interface ResourceDepot> { + + public R get(I resourceId); + + public R post(R resource); + + public R put(R resource); + + public void delete(I resourceId); +} diff --git a/wsdef/src/main/java/com/google/gson/rest/definition/ResourceMap.java b/wsdef/src/main/java/com/google/gson/rest/definition/ResourceMap.java index f8601d94..9cce3187 100644 --- a/wsdef/src/main/java/com/google/gson/rest/definition/ResourceMap.java +++ b/wsdef/src/main/java/com/google/gson/rest/definition/ResourceMap.java @@ -32,9 +32,9 @@ public final class ResourceMap { private final Map resources = new HashMap(); - public Builder set(CallPath callPath, RestCallSpec spec) { - Preconditions.checkArgument(resources.get(callPath) == null); - resources.put(callPath, spec); + public Builder set(CallPath baseCallPath, RestCallSpec spec) { + Preconditions.checkArgument(resources.get(baseCallPath) == null); + resources.put(baseCallPath, spec); return this; } diff --git a/wsdef/src/main/java/com/google/gson/rest/definition/RestCallSpec.java b/wsdef/src/main/java/com/google/gson/rest/definition/RestCallSpec.java index 0953f70b..bf54f487 100644 --- a/wsdef/src/main/java/com/google/gson/rest/definition/RestCallSpec.java +++ b/wsdef/src/main/java/com/google/gson/rest/definition/RestCallSpec.java @@ -130,4 +130,9 @@ public final class RestCallSpec { "path: %s, version: %.2f, resourceType: %s, requestSpec: %s, responseSpec: %s", path, version, resourceType, requestSpec, responseSpec); } + + public RestCallSpec createCopy(CallPath callPath) { + return new RestCallSpec(supportedHttpMethods, callPath, requestSpec, + responseSpec, resourceType, version); + } } diff --git a/wsdef/src/main/java/com/google/gson/rest/definition/RestRequest.java b/wsdef/src/main/java/com/google/gson/rest/definition/RestRequest.java index eb7f4354..1f41bd81 100644 --- a/wsdef/src/main/java/com/google/gson/rest/definition/RestRequest.java +++ b/wsdef/src/main/java/com/google/gson/rest/definition/RestRequest.java @@ -32,19 +32,21 @@ public final class RestRequest> { private final HttpMethod method; private final HeaderMap headers; + private final I id; private final R body; private final RestRequestSpec spec; public RestRequest(HttpMethod method, HeaderMap requestHeaders, - R requestBody, Type resourceType) { + I resourceId, R requestBody, Type resourceType) { this.method = method; + this.id = resourceId; this.body = requestBody; this.headers = requestHeaders; this.spec = new RestRequestSpec(requestHeaders.getSpec(), resourceType); } public I getId() { - return body.getId(); + return id; } public HttpMethod getMethod() { diff --git a/wsdef/src/main/java/com/google/gson/rest/definition/Id.java b/wsdef/src/main/java/com/google/gson/rest/definition/ValueBasedId.java similarity index 81% rename from wsdef/src/main/java/com/google/gson/rest/definition/Id.java rename to wsdef/src/main/java/com/google/gson/rest/definition/ValueBasedId.java index ceee974b..60e814f4 100644 --- a/wsdef/src/main/java/com/google/gson/rest/definition/Id.java +++ b/wsdef/src/main/java/com/google/gson/rest/definition/ValueBasedId.java @@ -35,13 +35,11 @@ import java.lang.reflect.WildcardType; * * @param type variable for the rest resource */ -public final class Id implements ID { - private static final long NULL_VALUE = -1; +public final class ValueBasedId implements ID { private final long value; private final Type typeOfId; - private Id(long value, Type typeOfId) { - Preconditions.checkArgument(value != NULL_VALUE); + private ValueBasedId(long value, Type typeOfId) { this.value = value; this.typeOfId = typeOfId; } @@ -51,8 +49,8 @@ public final class Id implements ID { return value; } - public static long getValue(Id id) { - return id == null ? NULL_VALUE : id.getValue(); + public static long getValue(ValueBasedId id) { + return id == null ? INVALID_ID : id.getValue(); } public String getValueAsString() { @@ -68,8 +66,8 @@ public final class Id implements ID { return (int) value; } - public static boolean isValid(Id id) { - return id != null && id.value != NULL_VALUE; + public static boolean isValid(ValueBasedId id) { + return id != null && id.value != INVALID_ID; } /** @@ -78,7 +76,8 @@ public final class Id implements ID { * only id values, not their types. Note that this shortcut doesn't work if you pass raw ids * to this method */ - public static boolean equals(/* @Nullable */ Id id1, /* @Nullable */ Id id2) { + public static boolean equals(/* @Nullable */ ValueBasedId id1, + /* @Nullable */ ValueBasedId id2) { if ((id1 == null && id2 != null) || (id1 != null && id2 == null)) { return false; } @@ -94,7 +93,7 @@ public final class Id implements ID { if (obj == null) return false; if (getClass() != obj.getClass()) return false; @SuppressWarnings("unchecked") - Id other = (Id)obj; + ValueBasedId other = (ValueBasedId)obj; if (typeOfId == null) { if (other.typeOfId != null) return false; } else if (!equivalentTypes(typeOfId, other.typeOfId)) return false; @@ -135,8 +134,8 @@ public final class Id implements ID { return true; } - public static Id get(long value, Type typeOfId) { - return new Id(value, typeOfId); + public static ValueBasedId get(long value, Type typeOfId) { + return new ValueBasedId(value, typeOfId); } @Override @@ -179,17 +178,18 @@ public final class Id implements ID { * @author inder * */ - public static final class GsonTypeAdapter implements JsonSerializer>, - JsonDeserializer> { + public static final class GsonTypeAdapter implements JsonSerializer>, + JsonDeserializer> { @Override - public JsonElement serialize(Id src, Type typeOfSrc, JsonSerializationContext context) { + public JsonElement serialize(ValueBasedId src, Type typeOfSrc, + JsonSerializationContext context) { return new JsonPrimitive(src.getValue()); } @Override - public Id deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { + public ValueBasedId deserialize(JsonElement json, Type typeOfT, + JsonDeserializationContext context) throws JsonParseException { if (!(typeOfT instanceof ParameterizedType)) { throw new JsonParseException("Id of unknown type: " + typeOfT); } @@ -197,7 +197,7 @@ public final class Id implements ID { // Since Id takes only one TypeVariable, the actual type corresponding to the first // TypeVariable is the Type we are looking for Type typeOfId = parameterizedType.getActualTypeArguments()[0]; - return Id.get(json.getAsLong(), typeOfId); + return ValueBasedId.get(json.getAsLong(), typeOfId); } } } diff --git a/wsdef/src/main/java/com/google/gson/webservice/definition/CallPath.java b/wsdef/src/main/java/com/google/gson/webservice/definition/CallPath.java index d9ed1b86..dc0c9380 100644 --- a/wsdef/src/main/java/com/google/gson/webservice/definition/CallPath.java +++ b/wsdef/src/main/java/com/google/gson/webservice/definition/CallPath.java @@ -15,6 +15,9 @@ */ package com.google.gson.webservice.definition; +import com.google.gson.rest.definition.ID; +import com.google.gson.webservice.definition.internal.utils.Pair; + /** * Encapsulation of a Web service path that is sent by the client. * @@ -22,31 +25,60 @@ package com.google.gson.webservice.definition; */ public final class CallPath { + private static final double IGNORE_VERSION = -1D; private final String path; private final double version; + private final long resourceId; public CallPath(String path) { if (path == null) { this.path = null; - version = -1D; + version = IGNORE_VERSION; + resourceId = ID.INVALID_ID; } else { - int index1 = path.indexOf('/'); - int index2 = path.substring(index1+1).indexOf('/'); - String versionStr = path.substring(index1+1, index2+1); - String callPathStr = path; - double givenVersion = -1D; - try { - // Skip over the version number from the URL - givenVersion = Double.parseDouble(versionStr); - callPathStr = path.substring(index2+1); - } catch (NumberFormatException e) { - // Assume that version number wasn't specified - } - this.path = callPathStr; - this.version = givenVersion; + Pair path2 = extractVersion(path); + this.version = path2.first; + Pair path3 = extractId(path2.second); + this.resourceId = path3.first; + this.path = path3.second; } } + /** + * Returns path after consuming version number from the begining + */ + private static Pair extractVersion(String path) { + int index1 = path.indexOf('/'); + int index2 = path.substring(index1+1).indexOf('/'); + String versionStr = path.substring(index1+1, index2+1); + double extractedVersion = -1.0D; + String revisedPath = path; + try { + // Skip over the version number from the URL + extractedVersion = Double.parseDouble(versionStr); + revisedPath = path.substring(index2+1); + } catch (NumberFormatException e) { + // Assume that version number wasn't specified + } + return Pair.create(extractedVersion, revisedPath); + } + + private static Pair extractId(String path) { + Pair originalPath = Pair.create(ID.INVALID_ID, path); + int end = path.endsWith("/") ? path.length() - 1 : path.length(); + int begin = path.substring(0, end-1).lastIndexOf('/') + 1; + if (begin < 0 || end < 0 || begin >= end) { + return originalPath; + } + try { + String id = path.substring(begin, end); + String pathWithoutId = path.substring(0, begin-1); + return Pair.create(Long.parseLong(id), pathWithoutId); + } catch (NumberFormatException e) { + return originalPath; + } + } + public String get() { return path; } @@ -55,11 +87,19 @@ public final class CallPath { return version; } + public long getResourceId() { + return resourceId; + } + @Override public int hashCode() { return path.hashCode(); } + public boolean matches(CallPath callPath) { + return path.startsWith(callPath.get()); + } + @Override public boolean equals(Object obj) { if (this == obj) { @@ -77,6 +117,6 @@ public final class CallPath { @Override public String toString() { - return path; + return String.format("path:%s, version:%2.f, resourceId: %d", path, version, resourceId); } } diff --git a/wsdef/src/main/java/com/google/gson/webservice/definition/HttpMethod.java b/wsdef/src/main/java/com/google/gson/webservice/definition/HttpMethod.java index 753841fa..705c3e09 100644 --- a/wsdef/src/main/java/com/google/gson/webservice/definition/HttpMethod.java +++ b/wsdef/src/main/java/com/google/gson/webservice/definition/HttpMethod.java @@ -36,4 +36,10 @@ public enum HttpMethod { public static final List ALL_METHODS = Collections.unmodifiableList(Arrays.asList(values())); -} \ No newline at end of file + + /** + * This header is used to indicate the real method that is channeled through the current + * request. For example, you can use it to send PUT requests under a POST. + */ + public static final String SIMULATED_METHOD_HEADER = "SimulatedMethod"; +} diff --git a/wsdef/src/main/java/com/google/gson/webservice/definition/internal/utils/Pair.java b/wsdef/src/main/java/com/google/gson/webservice/definition/internal/utils/Pair.java new file mode 100644 index 00000000..d3454d8d --- /dev/null +++ b/wsdef/src/main/java/com/google/gson/webservice/definition/internal/utils/Pair.java @@ -0,0 +1,64 @@ +/* + * 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.internal.utils; + +/** + * A simple object that holds onto a pair of object references, first and second. + * + * @author Inderjeet Singh + * + * @param + * @param + */ +public final class Pair { + + public final FIRST first; + public final SECOND second; + + public Pair(FIRST first, SECOND second) { + this.first = first; + this.second = second; + } + + public static Pair create(F first, S second) { + return new Pair(first, second); + } + + @Override + public int hashCode() { + return 17 * ((first != null) ? first.hashCode() : 0) + + 17 * ((second != null) ? second.hashCode() : 0); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Pair)) { + return false; + } + + Pair that = (Pair) o; + return equal(this.first, that.first) && equal(this.second, that.second); + } + + private static boolean equal(Object a, Object b) { + return a == b || (a != null && a.equals(b)); + } + + @Override + public String toString() { + return String.format("{%s,%s}", first, second); + } +} diff --git a/wsdef/src/test/java/com/google/gson/rest/definition/IdTest.java b/wsdef/src/test/java/com/google/gson/rest/definition/IdTest.java index cb8e07ab..9a2e72bd 100644 --- a/wsdef/src/test/java/com/google/gson/rest/definition/IdTest.java +++ b/wsdef/src/test/java/com/google/gson/rest/definition/IdTest.java @@ -20,34 +20,34 @@ import java.lang.reflect.ParameterizedType; import junit.framework.TestCase; import com.google.gson.reflect.TypeToken; -import com.google.gson.rest.definition.Id; +import com.google.gson.rest.definition.ValueBasedId; /** - * Unit test for {@link Id} + * Unit test for {@link ValueBasedId} * * @author inder */ public class IdTest extends TestCase { public void testRawTypeNotEqualToParameterizedOfConcreteType() { - ParameterizedType type = (ParameterizedType) new TypeToken>(){}.getType(); - assertFalse(Id.areEquivalentTypes(type, Id.class)); + ParameterizedType type = (ParameterizedType) new TypeToken>(){}.getType(); + assertFalse(ValueBasedId.areEquivalentTypes(type, ValueBasedId.class)); } public void testRawTypeEqualToParameterizedOfWildcardType() { - ParameterizedType fooType = (ParameterizedType) new TypeToken>(){}.getType(); - assertTrue(Id.areEquivalentTypes(fooType, Id.class)); + ParameterizedType fooType = (ParameterizedType) new TypeToken>(){}.getType(); + assertTrue(ValueBasedId.areEquivalentTypes(fooType, ValueBasedId.class)); } public void testStaticEquals() { - Id id1 = Id.get(3L, Foo.class); - Id id2 = Id.get(3L, Foo.class); - Id id3 = Id.get(4L, Foo.class); - assertTrue(Id.equals(id1, id2)); - assertFalse(Id.equals(null, id2)); - assertFalse(Id.equals(id1, null)); - assertTrue(Id.equals(null, null)); - assertFalse(Id.equals(id1, id3)); + ValueBasedId id1 = ValueBasedId.get(3L, Foo.class); + ValueBasedId id2 = ValueBasedId.get(3L, Foo.class); + ValueBasedId id3 = ValueBasedId.get(4L, Foo.class); + assertTrue(ValueBasedId.equals(id1, id2)); + assertFalse(ValueBasedId.equals(null, id2)); + assertFalse(ValueBasedId.equals(id1, null)); + assertTrue(ValueBasedId.equals(null, null)); + assertFalse(ValueBasedId.equals(id1, id3)); } private static class Foo { diff --git a/wsdef/src/test/java/com/google/gson/rest/definition/IdTypeAdapterTest.java b/wsdef/src/test/java/com/google/gson/rest/definition/IdTypeAdapterTest.java index 4da0bc50..98f4b5ea 100644 --- a/wsdef/src/test/java/com/google/gson/rest/definition/IdTypeAdapterTest.java +++ b/wsdef/src/test/java/com/google/gson/rest/definition/IdTypeAdapterTest.java @@ -25,21 +25,21 @@ import junit.framework.TestCase; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; -import com.google.gson.rest.definition.Id; +import com.google.gson.rest.definition.ValueBasedId; /** - * Unit tests for {@link Id.GsonTypeAdapter} + * Unit tests for {@link ValueBasedId.GsonTypeAdapter} * * @author inder */ public class IdTypeAdapterTest extends TestCase { - private static final Id STUDENT1_ID = Id.get(5L, Student.class); - private static final Id STUDENT2_ID = Id.get(6L, Student.class); + private static final ValueBasedId STUDENT1_ID = ValueBasedId.get(5L, Student.class); + private static final ValueBasedId STUDENT2_ID = ValueBasedId.get(6L, Student.class); private static final Student STUDENT1 = new Student(STUDENT1_ID, "first"); private static final Student STUDENT2 = new Student(STUDENT2_ID, "second"); private static final Type TYPE_COURSE_HISTORY = new TypeToken>(){}.getType(); - private static final Id> COURSE_ID = Id.get(10L, TYPE_COURSE_HISTORY); + private static final ValueBasedId> COURSE_ID = ValueBasedId.get(10L, TYPE_COURSE_HISTORY); private Gson gson; private Course course; @@ -47,7 +47,7 @@ public class IdTypeAdapterTest extends TestCase { @Override protected void setUp() { gson = new GsonBuilder() - .registerTypeAdapter(Id.class, new Id.GsonTypeAdapter()) + .registerTypeAdapter(ValueBasedId.class, new ValueBasedId.GsonTypeAdapter()) .create(); course = new Course(COURSE_ID, 4, new Assignment(null, null), createList(STUDENT1, STUDENT2)); @@ -71,13 +71,13 @@ public class IdTypeAdapterTest extends TestCase { @SuppressWarnings("unused") private static class Student { - Id id; + ValueBasedId id; String name; private Student() { this(null, null); } - public Student(Id id, String name) { + public Student(ValueBasedId id, String name) { this.id = id; this.name = name; } @@ -85,7 +85,7 @@ public class IdTypeAdapterTest extends TestCase { @SuppressWarnings("unused") private static class Course { final List students; - private final Id> courseId; + private final ValueBasedId> courseId; private final int numAssignments; private final Assignment assignment; @@ -93,14 +93,14 @@ public class IdTypeAdapterTest extends TestCase { this(null, 0, null, new ArrayList()); } - public Course(Id> courseId, int numAssignments, + public Course(ValueBasedId> courseId, int numAssignments, Assignment assignment, List players) { this.courseId = courseId; this.numAssignments = numAssignments; this.assignment = assignment; this.students = players; } - public Id> getId() { + public ValueBasedId> getId() { return courseId; } List getStudents() { @@ -110,13 +110,13 @@ public class IdTypeAdapterTest extends TestCase { @SuppressWarnings("unused") private static class Assignment { - private final Id> id; + private final ValueBasedId> id; private final T data; private Assignment() { this(null, null); } - public Assignment(Id> id, T data) { + public Assignment(ValueBasedId> id, T data) { this.id = id; this.data = data; } diff --git a/wsdef/src/test/java/com/google/gson/webservice/definition/CallPathTest.java b/wsdef/src/test/java/com/google/gson/webservice/definition/CallPathTest.java index f4fea72e..c9181496 100644 --- a/wsdef/src/test/java/com/google/gson/webservice/definition/CallPathTest.java +++ b/wsdef/src/test/java/com/google/gson/webservice/definition/CallPathTest.java @@ -17,17 +17,43 @@ package com.google.gson.webservice.definition; import junit.framework.TestCase; +/** + * Unit test for {@link CallPath} + * + * @author inder + */ public class CallPathTest extends TestCase { public void testVersionIsSkipped() { CallPath path = new CallPath("/1.0/rest/service1"); assertEquals("/rest/service1", path.get()); assertEquals(1D, path.getVersion()); + assertEquals(-1L, path.getResourceId()); } public void testVersionNotPresent() { CallPath path = new CallPath("/rest/service1"); assertEquals("/rest/service1", path.get()); assertEquals(-1D, path.getVersion()); + assertEquals(-1L, path.getResourceId()); + } + + public void testResourceIdPresent() { + CallPath path = new CallPath("/rest/service/3"); + assertEquals("/rest/service", path.get()); + assertEquals(3L, path.getResourceId()); + } + + public void testResourceIdWithEndSlashPresent() { + CallPath path = new CallPath("/rest/service/3/"); + assertEquals("/rest/service", path.get()); + assertEquals(3L, path.getResourceId()); + } + + public void testVersionAndResourceIdPresent() { + CallPath path = new CallPath("/3.1/rest/service53/323222"); + assertEquals(3.1D, path.getVersion()); + assertEquals("/rest/service53", path.get()); + assertEquals(323222L, path.getResourceId()); } } diff --git a/wsf/src/main/java/com/google/gson/rest/server/IdMap.java b/wsf/src/main/java/com/google/gson/rest/server/IdMap.java index 0252a460..3e03b720 100644 --- a/wsf/src/main/java/com/google/gson/rest/server/IdMap.java +++ b/wsf/src/main/java/com/google/gson/rest/server/IdMap.java @@ -34,6 +34,7 @@ import com.google.gson.rest.definition.IDFactory; */ public class IdMap> { public static final Logger LOG = Logger.getLogger(IdMap.class.getName()); + public static final long ID_START_VALUE = 1L; protected final Map map; private volatile long nextAvailableId; private final IDFactory idFactory; @@ -43,7 +44,7 @@ public class IdMap> { */ protected IdMap(Class classOfI, Type typeOfId) { map = new ConcurrentHashMap(); - nextAvailableId = 0; + nextAvailableId = ID_START_VALUE; this.idFactory = new IDFactory(classOfI, typeOfId); } diff --git a/wsf/src/main/java/com/google/gson/rest/server/RestRequestReceiver.java b/wsf/src/main/java/com/google/gson/rest/server/RestRequestReceiver.java index 74134ee5..7461415d 100644 --- a/wsf/src/main/java/com/google/gson/rest/server/RestRequestReceiver.java +++ b/wsf/src/main/java/com/google/gson/rest/server/RestRequestReceiver.java @@ -50,13 +50,17 @@ public final class RestRequestReceiver receive(HttpServletRequest request) { + public RestRequest receive(HttpServletRequest request, I resourceId) { try { HeaderMap requestParams = buildRequestParams(request); R requestBody = buildRequestBody(request); - + HttpMethod method = HttpMethod.getMethod(request.getMethod()); - return new RestRequest(method, requestParams, requestBody, spec.getResourceType()); + String simulatedMethod = request.getHeader(HttpMethod.SIMULATED_METHOD_HEADER); + if (simulatedMethod != null && !simulatedMethod.equals("")) { + method = HttpMethod.getMethod(simulatedMethod); + } + return new RestRequest(method, requestParams, resourceId, requestBody, spec.getResourceType()); } catch (IOException e) { throw new WebServiceSystemException(e); } catch (JsonParseException e) {