Revised RestClient to provide easy access to RestMethods.
Updated Order and Cart to be rest resources. Added a Queryable interface that can be used to indicate that a Rest Resource supports querying. Added getValueAsString() method to Id. Removed spurious warnings.
This commit is contained in:
parent
7e05cde813
commit
399d49c0e8
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2008 Google Inc.
|
||||
* 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.
|
||||
|
@ -15,105 +15,73 @@
|
|||
*/
|
||||
package com.google.gson.rest.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.rest.definition.ID;
|
||||
import com.google.gson.rest.definition.RestCallSpec;
|
||||
import com.google.gson.rest.definition.RestRequest;
|
||||
import com.google.gson.rest.definition.RestResource;
|
||||
import com.google.gson.rest.definition.RestResponse;
|
||||
import com.google.gson.webservice.definition.WebServiceSystemException;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Main class used by clients to access a Gson Web service.
|
||||
* A client class to access a rest resource
|
||||
*
|
||||
* @author inder
|
||||
* @author Inderjeet Singh
|
||||
*/
|
||||
public class RestClient {
|
||||
private final RestServerConfig config;
|
||||
private final Logger logger;
|
||||
private final Level logLevel;
|
||||
public class RestClient<I extends ID, R extends RestResource<I, R>> {
|
||||
private final RestClientStub stub;
|
||||
private final RestCallSpec callSpec;
|
||||
private final Type resourceType;
|
||||
|
||||
public RestClient(RestServerConfig serverConfig) {
|
||||
this(serverConfig, null);
|
||||
public RestClient(RestClientStub stub, CallPath callPath, Type resourceType) {
|
||||
this(stub, resourceType, generateRestCallSpec(callPath, resourceType));
|
||||
}
|
||||
|
||||
public RestClient(RestServerConfig serverConfig, Level logLevel) {
|
||||
this.config = serverConfig;
|
||||
this.logger = logLevel == null ? null : Logger.getLogger(RestClient.class.getName());
|
||||
this.logLevel = logLevel;
|
||||
protected RestClient(RestClientStub stub, Type resourceType, RestCallSpec callSpec) {
|
||||
this.stub = stub;
|
||||
this.callSpec = callSpec;
|
||||
this.resourceType = resourceType;
|
||||
}
|
||||
|
||||
private URL getWebServiceUrl(RestCallSpec callSpec) {
|
||||
double version = callSpec.getVersion();
|
||||
String versionPath = version == -1 ? "" : "/" + version;
|
||||
String url = config.getServiceBaseUrl() + versionPath + callSpec.getPath().get();
|
||||
try {
|
||||
return new URL(url);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
private static <T> RestCallSpec generateRestCallSpec(CallPath callPath, Type resourceType) {
|
||||
return new RestCallSpec.Builder(callPath, resourceType).build();
|
||||
}
|
||||
|
||||
public <I extends ID, R extends RestResource<I, R>> RestResponse<I, R> getResponse(
|
||||
RestCallSpec callSpec, RestRequest<I, R> request) {
|
||||
Gson gson = new GsonBuilder().setVersion(callSpec.getVersion()).create();
|
||||
return getResponse(callSpec, request, gson);
|
||||
public R get(I resourceId) {
|
||||
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);
|
||||
return response.getBody();
|
||||
}
|
||||
|
||||
public <I extends ID, R extends RestResource<I, R>> RestResponse<I, R> getResponse(
|
||||
RestCallSpec callSpec, RestRequest<I, R> request, Gson gson) {
|
||||
HttpURLConnection conn = null;
|
||||
try {
|
||||
URL webServiceUrl = getWebServiceUrl(callSpec);
|
||||
if (logger != null) {
|
||||
logger.log(logLevel, "Opening connection to " + webServiceUrl);
|
||||
}
|
||||
conn = (HttpURLConnection) webServiceUrl.openConnection();
|
||||
return getResponse(callSpec, request, gson, conn);
|
||||
} catch (IOException e) {
|
||||
throw new WebServiceSystemException(e);
|
||||
} finally {
|
||||
closeIgnoringErrors(conn);
|
||||
}
|
||||
public R post(R resource) {
|
||||
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);
|
||||
return response.getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method if you want to mange the HTTP Connection yourself. This is useful when you
|
||||
* 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) {
|
||||
try {
|
||||
if (logger != null) {
|
||||
URL webServiceUrl = getWebServiceUrl(callSpec);
|
||||
logger.log(logLevel, "Opening connection to " + webServiceUrl);
|
||||
}
|
||||
RestRequestSender requestSender = new RestRequestSender(gson, logLevel);
|
||||
requestSender.send(conn, request);
|
||||
RestResponseReceiver<I, R> responseReceiver =
|
||||
new RestResponseReceiver<I, R>(gson, callSpec.getResponseSpec(), logLevel);
|
||||
return responseReceiver.receive(conn);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new WebServiceSystemException(e);
|
||||
}
|
||||
public R put(R resource) {
|
||||
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);
|
||||
return response.getBody();
|
||||
}
|
||||
|
||||
private static void closeIgnoringErrors(HttpURLConnection conn) {
|
||||
if (conn != null) {
|
||||
conn.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("config:%s", config);
|
||||
public void delete(I resourceId) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* 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.client;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.rest.definition.ID;
|
||||
import com.google.gson.rest.definition.RestCallSpec;
|
||||
import com.google.gson.rest.definition.RestRequest;
|
||||
import com.google.gson.rest.definition.RestResource;
|
||||
import com.google.gson.rest.definition.RestResponse;
|
||||
import com.google.gson.webservice.definition.WebServiceSystemException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* A stub to access the rest service
|
||||
*
|
||||
* @author inder
|
||||
*/
|
||||
public class RestClientStub {
|
||||
private final RestServerConfig config;
|
||||
private final Logger logger;
|
||||
private final Level logLevel;
|
||||
|
||||
public RestClientStub(RestServerConfig serverConfig) {
|
||||
this(serverConfig, null);
|
||||
}
|
||||
|
||||
public RestClientStub(RestServerConfig serverConfig, Level logLevel) {
|
||||
this.config = serverConfig;
|
||||
this.logger = logLevel == null ? null : Logger.getLogger(RestClientStub.class.getName());
|
||||
this.logLevel = logLevel;
|
||||
}
|
||||
|
||||
private <I extends ID> URL getWebServiceUrl(
|
||||
RestCallSpec callSpec, ID id) {
|
||||
double version = callSpec.getVersion();
|
||||
StringBuilder url = new StringBuilder(config.getServiceBaseUrl());
|
||||
if (version != -1D) {
|
||||
url.append('/').append(version);
|
||||
}
|
||||
url.append(callSpec.getPath().get());
|
||||
if (id != null) {
|
||||
url.append('/').append(id.getValue());
|
||||
}
|
||||
try {
|
||||
return new URL(url.toString());
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public <I extends ID, R extends RestResource<I, R>> RestResponse<I, R> getResponse(
|
||||
RestCallSpec callSpec, RestRequest<I, R> request) {
|
||||
Gson gson = new GsonBuilder().setVersion(callSpec.getVersion()).create();
|
||||
return getResponse(callSpec, request, gson);
|
||||
}
|
||||
|
||||
public <I extends ID, R extends RestResource<I, R>> RestResponse<I, R> getResponse(
|
||||
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);
|
||||
}
|
||||
conn = (HttpURLConnection) webServiceUrl.openConnection();
|
||||
return getResponse(callSpec, request, gson, conn);
|
||||
} catch (IOException e) {
|
||||
throw new WebServiceSystemException(e);
|
||||
} finally {
|
||||
closeIgnoringErrors(conn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method if you want to mange the HTTP Connection yourself. This is useful when you
|
||||
* 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) {
|
||||
try {
|
||||
if (logger != null) {
|
||||
URL webServiceUrl = getWebServiceUrl(callSpec, getId(request.getBody()));
|
||||
logger.log(logLevel, "Opening connection to " + webServiceUrl);
|
||||
}
|
||||
RestRequestSender requestSender = new RestRequestSender(gson, logLevel);
|
||||
requestSender.send(conn, request);
|
||||
RestResponseReceiver<I, R> responseReceiver =
|
||||
new RestResponseReceiver<I, R>(gson, callSpec.getResponseSpec(), logLevel);
|
||||
return responseReceiver.receive(conn);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new WebServiceSystemException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void closeIgnoringErrors(HttpURLConnection conn) {
|
||||
if (conn != null) {
|
||||
conn.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -25,7 +25,9 @@ import java.util.concurrent.Executors;
|
|||
*/
|
||||
final class SingleThreadExecutor implements TaskExecutor {
|
||||
private ExecutorService executor;
|
||||
public void execute(final Runnable r) {
|
||||
|
||||
@Override
|
||||
public void execute(Runnable r) {
|
||||
executor = Executors.newSingleThreadExecutor();
|
||||
executor.execute(r);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,9 @@ package com.google.gson.webservice.client;
|
|||
*/
|
||||
final class ThreadPerTaskExecutor implements TaskExecutor {
|
||||
private Thread thread;
|
||||
public void execute(final Runnable r) {
|
||||
|
||||
@Override
|
||||
public void execute(Runnable r) {
|
||||
thread = new Thread(r);
|
||||
thread.start();
|
||||
}
|
||||
|
|
|
@ -54,6 +54,11 @@ public final class Id<R> implements ID {
|
|||
public static long getValue(Id<?> id) {
|
||||
return id == null ? NULL_VALUE : id.getValue();
|
||||
}
|
||||
|
||||
public String getValueAsString() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
public Type getTypeOfId() {
|
||||
return typeOfId;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
|
||||
package com.google.gson.rest.definition;
|
||||
|
||||
/**
|
||||
* Implement this interface in a service to indicate that it allows querying
|
||||
* by this type
|
||||
*
|
||||
* @author Inderjeet Singh
|
||||
*/
|
||||
public interface Queryable<QUERY, RESULTS> {
|
||||
public RESULTS query(QUERY query);
|
||||
}
|
|
@ -28,7 +28,7 @@ import com.google.gson.reflect.TypeToken;
|
|||
import com.google.gson.rest.definition.Id;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link IdTypeAdapter}
|
||||
* Unit tests for {@link Id.GsonTypeAdapter}
|
||||
*
|
||||
* @author inder
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/package com.google.gson.example.rest.client;
|
||||
|
||||
import com.google.gson.example.model.Cart;
|
||||
import com.google.gson.example.model.LineItem;
|
||||
import com.google.gson.example.model.Order;
|
||||
import com.google.gson.rest.client.RestClient;
|
||||
import com.google.gson.rest.client.RestClientStub;
|
||||
import com.google.gson.rest.client.RestServerConfig;
|
||||
import com.google.gson.rest.definition.Id;
|
||||
import com.google.gson.webservice.definition.CallPath;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* A sample client for the rest resource for {@link Order}
|
||||
*
|
||||
* @author Inderjeet Singh
|
||||
*/
|
||||
public class OrderClient {
|
||||
public static final CallPath CALL_PATH = new CallPath("/rest/order");
|
||||
private final RestClient<Id<Order>, Order> restClient;
|
||||
public OrderClient() {
|
||||
RestServerConfig serverConfig = new RestServerConfig("http://localhost");
|
||||
RestClientStub stub = new RestClientStub(serverConfig, Level.INFO);
|
||||
restClient = new RestClient<Id<Order>, Order>(stub, CALL_PATH, Order.class);
|
||||
}
|
||||
|
||||
public Order placeOrder(Cart cart) {
|
||||
Order order = new Order(cart, cart.getId().getValueAsString());
|
||||
return restClient.post(order);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
OrderClient client = new OrderClient();
|
||||
List<LineItem> lineItems = new ArrayList<LineItem>();
|
||||
lineItems.add(new LineItem("item1", 2, 1000000L, "USD"));
|
||||
Cart cart = new Cart(lineItems, "first last", "4111-1111-1111-1111");
|
||||
client.placeOrder(cart);
|
||||
}
|
||||
}
|
|
@ -15,6 +15,9 @@
|
|||
*/
|
||||
package com.google.gson.example.model;
|
||||
|
||||
import com.google.gson.rest.definition.Id;
|
||||
import com.google.gson.rest.definition.RestResource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -22,10 +25,11 @@ import java.util.List;
|
|||
*
|
||||
* @author inder
|
||||
*/
|
||||
public class Cart {
|
||||
public class Cart implements RestResource<Id<Cart>, Cart> {
|
||||
private final List<LineItem> lineItems;
|
||||
private final String buyerName;
|
||||
private final String creditCard;
|
||||
private Id<Cart> id;
|
||||
|
||||
public Cart(List<LineItem> lineItems, String buyerName, String creditCard) {
|
||||
this.lineItems = lineItems;
|
||||
|
@ -44,4 +48,19 @@ public class Cart {
|
|||
public String getCreditCard() {
|
||||
return creditCard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Id<Cart> getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setId(Id<Cart> id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasId() {
|
||||
return Id.isValid(id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,14 +15,18 @@
|
|||
*/
|
||||
package com.google.gson.example.model;
|
||||
|
||||
import com.google.gson.rest.definition.Id;
|
||||
import com.google.gson.rest.definition.RestResource;
|
||||
|
||||
/**
|
||||
* An order
|
||||
*
|
||||
* @author inder
|
||||
*/
|
||||
public class Order {
|
||||
public class Order implements RestResource<Id<Order>, Order> {
|
||||
public final Cart postedCart;
|
||||
public final String orderNumber;
|
||||
private Id<Order> id;
|
||||
|
||||
public Order(Cart postedCart, String orderNumber) {
|
||||
this.postedCart = postedCart;
|
||||
|
@ -37,4 +41,18 @@ public class Order {
|
|||
return orderNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Id<Order> getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setId(Id<Order> id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasId() {
|
||||
return Id.isValid(id);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user