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.
This commit is contained in:
Inderjeet Singh 2010-11-09 18:00:08 +00:00
parent 103edb9c36
commit be05420c6b
18 changed files with 318 additions and 102 deletions

View File

@ -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<I extends ID, R extends RestResource<I, R>> {
public class ResourceDepotClient<I extends ID, R extends RestResource<I, R>> implements ResourceDepot<I, R> {
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 <T> RestCallSpec generateRestCallSpec(CallPath callPath, Type resourceType) {
@ -54,8 +63,8 @@ public class RestClient<I extends ID, R extends RestResource<I, R>> {
HeaderMap requestHeaders =
new HeaderMap.Builder(callSpec.getRequestSpec().getHeadersSpec()).build();
RestRequest<I, R> request =
new RestRequest<I, R>(HttpMethod.GET, requestHeaders, null, resourceType);
RestResponse<I, R> response = stub.getResponse(callSpec, request);
new RestRequest<I, R>(HttpMethod.GET, requestHeaders, resourceId, null, resourceType);
RestResponse<I, R> response = stub.getResponse(callSpec, request, gson);
return response.getBody();
}
@ -63,8 +72,8 @@ public class RestClient<I extends ID, R extends RestResource<I, R>> {
HeaderMap requestHeaders =
new HeaderMap.Builder(callSpec.getRequestSpec().getHeadersSpec()).build();
RestRequest<I, R> request =
new RestRequest<I, R>(HttpMethod.POST, requestHeaders, resource, resourceType);
RestResponse<I, R> response = stub.getResponse(callSpec, request);
new RestRequest<I, R>(HttpMethod.POST, requestHeaders, resource.getId(), resource, resourceType);
RestResponse<I, R> response = stub.getResponse(callSpec, request, gson);
return response.getBody();
}
@ -72,8 +81,8 @@ public class RestClient<I extends ID, R extends RestResource<I, R>> {
HeaderMap requestHeaders =
new HeaderMap.Builder(callSpec.getRequestSpec().getHeadersSpec()).build();
RestRequest<I, R> request =
new RestRequest<I, R>(HttpMethod.PUT, requestHeaders, resource, resourceType);
RestResponse<I, R> response = stub.getResponse(callSpec, request);
new RestRequest<I, R>(HttpMethod.PUT, requestHeaders, resource.getId(), resource, resourceType);
RestResponse<I, R> response = stub.getResponse(callSpec, request, gson);
return response.getBody();
}
@ -81,7 +90,7 @@ public class RestClient<I extends ID, R extends RestResource<I, R>> {
HeaderMap requestHeaders =
new HeaderMap.Builder(callSpec.getRequestSpec().getHeadersSpec()).build();
RestRequest<I, R> request =
new RestRequest<I, R>(HttpMethod.DELETE, requestHeaders, null, resourceType);
stub.getResponse(callSpec, request);
new RestRequest<I, R>(HttpMethod.DELETE, requestHeaders, resourceId, null, resourceType);
stub.getResponse(callSpec, request, gson);
}
}

View File

@ -79,10 +79,7 @@ public class RestClientStub {
RestCallSpec callSpec, RestRequest<I, R> 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 <I extends ID, R extends RestResource<I, R>> RestResponse<I, R> getResponse(
RestCallSpec callSpec, RestRequest<I, R> request, Gson gson, HttpURLConnection conn) {
RestCallSpec callSpec, RestRequest<I, R> 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 extends ID, R extends RestResource<I, R>> I getId(R resource) {
return (resource == null || !resource.hasId()) ? null : resource.getId();
}
@Override
public String toString() {
return String.format("config:%s", config);

View File

@ -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 <I extends ID, R extends RestResource<I, R>> void send(HttpURLConnection conn, RestRequest<I, R> request) {
public <I extends ID, R extends RestResource<I, R>> void send(
HttpURLConnection conn, RestRequest<I, R> 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());
}

View File

@ -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();
}

View File

@ -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 <I>
*/
public class IDFactory<I extends ID> {
private final Class<? super I> classOfI;
private final Type typeOfId;
@ -11,9 +33,10 @@ public class IDFactory<I extends ID> {
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();
}

View File

@ -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<I extends ID, R extends RestResource<I, R>> {
public R get(I resourceId);
public R post(R resource);
public R put(R resource);
public void delete(I resourceId);
}

View File

@ -32,9 +32,9 @@ public final class ResourceMap {
private final Map<CallPath, RestCallSpec> resources =
new HashMap<CallPath, RestCallSpec>();
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;
}

View File

@ -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);
}
}

View File

@ -32,19 +32,21 @@ public final class RestRequest<I extends ID, R extends RestResource<I, R>> {
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() {

View File

@ -35,13 +35,11 @@ import java.lang.reflect.WildcardType;
*
* @param <R> type variable for the rest resource
*/
public final class Id<R> implements ID {
private static final long NULL_VALUE = -1;
public final class ValueBasedId<R> 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<R> 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<R> 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<R> 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 <T> boolean equals(/* @Nullable */ Id<T> id1, /* @Nullable */ Id<T> id2) {
public static <T> boolean equals(/* @Nullable */ ValueBasedId<T> id1,
/* @Nullable */ ValueBasedId<T> id2) {
if ((id1 == null && id2 != null) || (id1 != null && id2 == null)) {
return false;
}
@ -94,7 +93,7 @@ public final class Id<R> implements ID {
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
@SuppressWarnings("unchecked")
Id<R> other = (Id<R>)obj;
ValueBasedId<R> other = (ValueBasedId<R>)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<R> implements ID {
return true;
}
public static <RS> Id<RS> get(long value, Type typeOfId) {
return new Id<RS>(value, typeOfId);
public static <RS> ValueBasedId<RS> get(long value, Type typeOfId) {
return new ValueBasedId<RS>(value, typeOfId);
}
@Override
@ -179,17 +178,18 @@ public final class Id<R> implements ID {
* @author inder
*
*/
public static final class GsonTypeAdapter implements JsonSerializer<Id<?>>,
JsonDeserializer<Id<?>> {
public static final class GsonTypeAdapter implements JsonSerializer<ValueBasedId<?>>,
JsonDeserializer<ValueBasedId<?>> {
@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<R> 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);
}
}
}

View File

@ -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<Double, String> path2 = extractVersion(path);
this.version = path2.first;
Pair<Long, String> path3 = extractId(path2.second);
this.resourceId = path3.first;
this.path = path3.second;
}
}
/**
* Returns path after consuming version number from the begining
*/
private static Pair<Double, String> 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<Long, String> extractId(String path) {
Pair<Long, String> 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);
}
}

View File

@ -36,4 +36,10 @@ public enum HttpMethod {
public static final List<HttpMethod> ALL_METHODS =
Collections.unmodifiableList(Arrays.asList(values()));
}
/**
* 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";
}

View File

@ -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 <FIRST>
* @param <SECOND>
*/
public final class Pair<FIRST, SECOND> {
public final FIRST first;
public final SECOND second;
public Pair(FIRST first, SECOND second) {
this.first = first;
this.second = second;
}
public static <F, S> Pair<F, S> create(F first, S second) {
return new Pair<F, S>(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);
}
}

View File

@ -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<Id<Foo>>(){}.getType();
assertFalse(Id.areEquivalentTypes(type, Id.class));
ParameterizedType type = (ParameterizedType) new TypeToken<ValueBasedId<Foo>>(){}.getType();
assertFalse(ValueBasedId.areEquivalentTypes(type, ValueBasedId.class));
}
public void testRawTypeEqualToParameterizedOfWildcardType() {
ParameterizedType fooType = (ParameterizedType) new TypeToken<Id<?>>(){}.getType();
assertTrue(Id.areEquivalentTypes(fooType, Id.class));
ParameterizedType fooType = (ParameterizedType) new TypeToken<ValueBasedId<?>>(){}.getType();
assertTrue(ValueBasedId.areEquivalentTypes(fooType, ValueBasedId.class));
}
public void testStaticEquals() {
Id<Foo> id1 = Id.get(3L, Foo.class);
Id<Foo> id2 = Id.get(3L, Foo.class);
Id<Foo> 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<Foo> id1 = ValueBasedId.get(3L, Foo.class);
ValueBasedId<Foo> id2 = ValueBasedId.get(3L, Foo.class);
ValueBasedId<Foo> 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 {

View File

@ -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<Student> STUDENT1_ID = Id.get(5L, Student.class);
private static final Id<Student> STUDENT2_ID = Id.get(6L, Student.class);
private static final ValueBasedId<Student> STUDENT1_ID = ValueBasedId.get(5L, Student.class);
private static final ValueBasedId<Student> 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<Course<HistoryCourse>>(){}.getType();
private static final Id<Course<HistoryCourse>> COURSE_ID = Id.get(10L, TYPE_COURSE_HISTORY);
private static final ValueBasedId<Course<HistoryCourse>> COURSE_ID = ValueBasedId.get(10L, TYPE_COURSE_HISTORY);
private Gson gson;
private Course<HistoryCourse> 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<HistoryCourse>(COURSE_ID, 4,
new Assignment<HistoryCourse>(null, null), createList(STUDENT1, STUDENT2));
@ -71,13 +71,13 @@ public class IdTypeAdapterTest extends TestCase {
@SuppressWarnings("unused")
private static class Student {
Id<Student> id;
ValueBasedId<Student> id;
String name;
private Student() {
this(null, null);
}
public Student(Id<Student> id, String name) {
public Student(ValueBasedId<Student> id, String name) {
this.id = id;
this.name = name;
}
@ -85,7 +85,7 @@ public class IdTypeAdapterTest extends TestCase {
@SuppressWarnings("unused")
private static class Course<T> {
final List<Student> students;
private final Id<Course<T>> courseId;
private final ValueBasedId<Course<T>> courseId;
private final int numAssignments;
private final Assignment<T> assignment;
@ -93,14 +93,14 @@ public class IdTypeAdapterTest extends TestCase {
this(null, 0, null, new ArrayList<Student>());
}
public Course(Id<Course<T>> courseId, int numAssignments,
public Course(ValueBasedId<Course<T>> courseId, int numAssignments,
Assignment<T> assignment, List<Student> players) {
this.courseId = courseId;
this.numAssignments = numAssignments;
this.assignment = assignment;
this.students = players;
}
public Id<Course<T>> getId() {
public ValueBasedId<Course<T>> getId() {
return courseId;
}
List<Student> getStudents() {
@ -110,13 +110,13 @@ public class IdTypeAdapterTest extends TestCase {
@SuppressWarnings("unused")
private static class Assignment<T> {
private final Id<Assignment<T>> id;
private final ValueBasedId<Assignment<T>> id;
private final T data;
private Assignment() {
this(null, null);
}
public Assignment(Id<Assignment<T>> id, T data) {
public Assignment(ValueBasedId<Assignment<T>> id, T data) {
this.id = id;
this.data = data;
}

View File

@ -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());
}
}

View File

@ -34,6 +34,7 @@ import com.google.gson.rest.definition.IDFactory;
*/
public class IdMap<I extends ID, T extends HasId<I>> {
public static final Logger LOG = Logger.getLogger(IdMap.class.getName());
public static final long ID_START_VALUE = 1L;
protected final Map<I, T> map;
private volatile long nextAvailableId;
private final IDFactory<I> idFactory;
@ -43,7 +44,7 @@ public class IdMap<I extends ID, T extends HasId<I>> {
*/
protected IdMap(Class<? super I> classOfI, Type typeOfId) {
map = new ConcurrentHashMap<I, T>();
nextAvailableId = 0;
nextAvailableId = ID_START_VALUE;
this.idFactory = new IDFactory<I>(classOfI, typeOfId);
}

View File

@ -50,13 +50,17 @@ public final class RestRequestReceiver<I extends ID, R extends RestResource<I, R
this.spec = spec;
}
public RestRequest<I, R> receive(HttpServletRequest request) {
public RestRequest<I, R> receive(HttpServletRequest request, I resourceId) {
try {
HeaderMap requestParams = buildRequestParams(request);
R requestBody = buildRequestBody(request);
HttpMethod method = HttpMethod.getMethod(request.getMethod());
return new RestRequest<I, R>(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<I, R>(method, requestParams, resourceId, requestBody, spec.getResourceType());
} catch (IOException e) {
throw new WebServiceSystemException(e);
} catch (JsonParseException e) {