Added an asynchronous client for invoking Web-services. This client uses a queue to hold web-service requests and returns the control back to the caller. A separate thread executes these web-service requests and invokes a caller-suplied callback with the results.
Made WebServiceClient reusable across different type of Web-service calls. Added configurable logging support in RequestSender, ResponseReceiver and WebServiceClients. Added some logging. Throwing WebServiceSystemException instead of RuntimException.
This commit is contained in:
parent
0a134db2f0
commit
e4e9254034
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.client;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
import com.google.gson.webservice.definition.WebServiceCall;
|
||||
import com.google.gson.webservice.definition.WebServiceResponse;
|
||||
import com.google.gson.webservice.definition.WebServiceSystemException;
|
||||
|
||||
/**
|
||||
* A consumer that executes in its own thread consuming queue entries and invoking web-service calls
|
||||
*
|
||||
* @author inder
|
||||
*/
|
||||
final class QueueConsumer implements Runnable {
|
||||
|
||||
private final BlockingQueue<QueueEntry> queue;
|
||||
private WebServiceClient client;
|
||||
|
||||
QueueConsumer(BlockingQueue<QueueEntry> queue, WebServiceClient client) {
|
||||
this.queue = queue;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while(true) {
|
||||
consume(queue.take());
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// exit
|
||||
}
|
||||
}
|
||||
|
||||
private void consume(QueueEntry entry) {
|
||||
try {
|
||||
WebServiceResponse response = client.getResponse(entry.callSpec, entry.request);
|
||||
WebServiceCall call = new WebServiceCall(entry.callSpec, entry.request, response);
|
||||
entry.responseCallback.handleResponse(call);
|
||||
} catch (WebServiceSystemException e) {
|
||||
entry.responseCallback.handleError(e, entry.request, entry.callSpec);
|
||||
}
|
||||
}
|
||||
}
|
@ -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.webservice.client;
|
||||
|
||||
import com.google.gson.webservice.definition.WebServiceCallSpec;
|
||||
import com.google.gson.webservice.definition.WebServiceRequest;
|
||||
|
||||
/**
|
||||
* A holder class for an entry stored in queue. It contains references to the request, callspec,
|
||||
* and the client-supplied callback to provide sufficient information to execute a web-service call.
|
||||
*
|
||||
* @author inder
|
||||
*/
|
||||
final class QueueEntry {
|
||||
final WebServiceCallSpec callSpec;
|
||||
final WebServiceRequest request;
|
||||
final ResponseCallback responseCallback;
|
||||
|
||||
QueueEntry(WebServiceCallSpec callSpec, WebServiceRequest request,
|
||||
ResponseCallback responseCallback) {
|
||||
this.callSpec = callSpec;
|
||||
this.request = request;
|
||||
this.responseCallback = responseCallback;
|
||||
}
|
||||
}
|
@ -21,12 +21,15 @@ import java.io.Writer;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.webservice.definition.HeaderMap;
|
||||
import com.google.gson.webservice.definition.HeaderMapSpec;
|
||||
import com.google.gson.webservice.definition.RequestBody;
|
||||
import com.google.gson.webservice.definition.WebServiceRequest;
|
||||
import com.google.gson.webservice.definition.WebServiceSystemException;
|
||||
|
||||
/**
|
||||
* Class to send Web service requests on a {@link HttpURLConnection}.
|
||||
@ -35,9 +38,16 @@ import com.google.gson.webservice.definition.WebServiceRequest;
|
||||
*/
|
||||
public final class RequestSender {
|
||||
private final Gson gson;
|
||||
private final Logger logger;
|
||||
private final Level logLevel;
|
||||
|
||||
public RequestSender(Gson gson) {
|
||||
this(gson, null);
|
||||
}
|
||||
public RequestSender(Gson gson, Level logLevel) {
|
||||
this.gson = gson;
|
||||
logger = logLevel == null ? null : Logger.getLogger(RequestSender.class.getName());
|
||||
this.logLevel = logLevel;
|
||||
}
|
||||
|
||||
public void send(HttpURLConnection conn, WebServiceRequest request) {
|
||||
@ -60,7 +70,7 @@ public final class RequestSender {
|
||||
// Initiate the sending of the request.
|
||||
conn.connect();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new WebServiceSystemException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,6 +82,9 @@ public final class RequestSender {
|
||||
Object value = entry.getValue();
|
||||
String json = gson.toJson(value, type);
|
||||
conn.addRequestProperty(paramName, json);
|
||||
if (logger != null) {
|
||||
logger.log(logLevel, String.format("Request param: %s:%s", paramName, json));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.client;
|
||||
|
||||
import com.google.gson.webservice.definition.WebServiceCall;
|
||||
import com.google.gson.webservice.definition.WebServiceCallSpec;
|
||||
import com.google.gson.webservice.definition.WebServiceRequest;
|
||||
import com.google.gson.webservice.definition.WebServiceSystemException;
|
||||
|
||||
/**
|
||||
* A client-supplied callback to be used with {@link WebServiceClientAsync}. When a web-service
|
||||
* call is executed asynchronously, this callback is invoked with the results.
|
||||
*
|
||||
* @author inder
|
||||
*/
|
||||
public interface ResponseCallback {
|
||||
public void handleResponse(WebServiceCall call);
|
||||
public void handleError(WebServiceSystemException e, WebServiceRequest request,
|
||||
WebServiceCallSpec callSpec);
|
||||
}
|
@ -22,6 +22,8 @@ import java.io.Reader;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.webservice.definition.HeaderMap;
|
||||
@ -39,10 +41,17 @@ import com.google.gson.webservice.definition.WebServiceResponse;
|
||||
public final class ResponseReceiver {
|
||||
private final Gson gson;
|
||||
private final ResponseSpec spec;
|
||||
private final Logger logger;
|
||||
private final Level logLevel;
|
||||
|
||||
public ResponseReceiver(Gson gson, ResponseSpec spec) {
|
||||
this(gson, spec, null);
|
||||
}
|
||||
public ResponseReceiver(Gson gson, ResponseSpec spec, Level logLevel) {
|
||||
this.gson = gson;
|
||||
this.spec = spec;
|
||||
this.logger = logLevel == null ? null : Logger.getLogger(ResponseReceiver.class.getName());
|
||||
this.logLevel = logLevel;
|
||||
}
|
||||
|
||||
public WebServiceResponse receive(HttpURLConnection conn) {
|
||||
@ -64,6 +73,9 @@ public final class ResponseReceiver {
|
||||
String paramName = entry.getKey();
|
||||
String json = conn.getHeaderField(paramName);
|
||||
if (json != null) {
|
||||
if (logger != null) {
|
||||
logger.log(logLevel, String.format("Response Header: %s:%s\n", paramName, json));
|
||||
}
|
||||
Type typeOfT = paramsSpec.getTypeFor(paramName);
|
||||
Object value = gson.fromJson(json, typeOfT);
|
||||
paramsBuilder.put(paramName, value, typeOfT);
|
||||
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.client;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* An executor that uses a single thread to execute all calls
|
||||
*
|
||||
* @author inder
|
||||
*/
|
||||
final class SingleThreadExecutor implements TaskExecutor {
|
||||
private ExecutorService executor;
|
||||
public void execute(final Runnable r) {
|
||||
executor = Executors.newSingleThreadExecutor();
|
||||
executor.execute(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdownNow() {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
}
|
@ -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.client;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* An {@link Executor} with an additional method for shutdown. We could have just used
|
||||
* {@link java.util.concurent.ExecutorService}, however, that requires too many methods to be
|
||||
* implemented.
|
||||
*
|
||||
* @author inder
|
||||
*/
|
||||
interface TaskExecutor extends Executor {
|
||||
public void shutdownNow();
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.client;
|
||||
|
||||
/**
|
||||
* An executor that uses a new thread to execute each call
|
||||
*
|
||||
* @author inder
|
||||
*/
|
||||
final class ThreadPerTaskExecutor implements TaskExecutor {
|
||||
private Thread thread;
|
||||
public void execute(final Runnable r) {
|
||||
thread = new Thread(r);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdownNow() {
|
||||
if (thread != null) {
|
||||
thread.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
@ -19,11 +19,17 @@ 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.webservice.definition.ResponseBody;
|
||||
import com.google.gson.webservice.definition.WebServiceCallSpec;
|
||||
import com.google.gson.webservice.definition.WebServiceRequest;
|
||||
import com.google.gson.webservice.definition.WebServiceResponse;
|
||||
import com.google.gson.webservice.definition.WebServiceSystemException;
|
||||
import com.google.gson.webservice.typeadapters.ResponseBodyGsonConverter;
|
||||
|
||||
/**
|
||||
* Main class used by clients to access a Gson Web service.
|
||||
@ -32,16 +38,20 @@ import com.google.gson.webservice.definition.WebServiceResponse;
|
||||
*/
|
||||
public final class WebServiceClient {
|
||||
private final WebServiceConfig config;
|
||||
private final WebServiceCallSpec callSpec;
|
||||
private final Gson gson;
|
||||
private final Logger logger;
|
||||
private final Level logLevel;
|
||||
|
||||
public WebServiceClient(Gson gson, WebServiceConfig serverConfig, WebServiceCallSpec callSpec) {
|
||||
this.gson = gson;
|
||||
this.config = serverConfig;
|
||||
this.callSpec = callSpec;
|
||||
public WebServiceClient(WebServiceConfig serverConfig) {
|
||||
this(serverConfig, null);
|
||||
}
|
||||
|
||||
private URL getWebServiceUrl() {
|
||||
public WebServiceClient(WebServiceConfig serverConfig, Level logLevel) {
|
||||
this.config = serverConfig;
|
||||
this.logger = logLevel == null ? null : Logger.getLogger(WebServiceClient.class.getName());
|
||||
this.logLevel = logLevel;
|
||||
}
|
||||
|
||||
private URL getWebServiceUrl(WebServiceCallSpec callSpec) {
|
||||
String url = config.getServiceBaseUrl() + callSpec.getPath().get();
|
||||
try {
|
||||
return new URL(url);
|
||||
@ -50,23 +60,31 @@ public final class WebServiceClient {
|
||||
}
|
||||
}
|
||||
|
||||
public WebServiceResponse getResponse(WebServiceRequest request) {
|
||||
public WebServiceResponse getResponse(WebServiceCallSpec callSpec, WebServiceRequest request) {
|
||||
try {
|
||||
HttpURLConnection conn = (HttpURLConnection) getWebServiceUrl().openConnection();
|
||||
URL webServiceUrl = getWebServiceUrl(callSpec);
|
||||
if (logger != null) {
|
||||
logger.log(logLevel, "Opening connection to " + webServiceUrl);
|
||||
}
|
||||
HttpURLConnection conn = (HttpURLConnection) webServiceUrl.openConnection();
|
||||
Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(ResponseBody.class,
|
||||
new ResponseBodyGsonConverter(callSpec.getResponseSpec().getBodySpec()))
|
||||
.create();
|
||||
RequestSender requestSender = new RequestSender(gson);
|
||||
requestSender.send(conn, request);
|
||||
ResponseReceiver responseReceiver = new ResponseReceiver(gson, callSpec.getResponseSpec());
|
||||
ResponseReceiver responseReceiver =
|
||||
new ResponseReceiver(gson, callSpec.getResponseSpec(), logLevel);
|
||||
return responseReceiver.receive(conn);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new WebServiceSystemException(e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new WebServiceSystemException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("{config:").append(config).append(",callSpec:").append(callSpec);
|
||||
sb.append("gson:").append(gson).append("}");
|
||||
return sb.toString();
|
||||
return String.format("config:%s", config);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.client;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.google.gson.webservice.definition.WebServiceCallSpec;
|
||||
import com.google.gson.webservice.definition.WebServiceRequest;
|
||||
import com.google.gson.webservice.definition.WebServiceSystemException;
|
||||
|
||||
/**
|
||||
* A client for invoking a JSON-based Web-service in an asynchronous manner. The call is queued,
|
||||
* and control returns to the caller. A separate thread executes the call, and invokes the
|
||||
* client-supplied callback with results.
|
||||
*
|
||||
* @author inder
|
||||
*/
|
||||
public final class WebServiceClientAsync {
|
||||
|
||||
private final BlockingQueue<QueueEntry> queue;
|
||||
private final boolean threadPerTask;
|
||||
private final TaskExecutor executor;
|
||||
|
||||
public WebServiceClientAsync(WebServiceConfig serverConfig) {
|
||||
this(serverConfig, null);
|
||||
}
|
||||
public WebServiceClientAsync(WebServiceConfig serverConfig, Level logLevel) {
|
||||
this(new WebServiceClient(serverConfig, logLevel));
|
||||
}
|
||||
|
||||
public WebServiceClientAsync(WebServiceClient client) {
|
||||
queue = new LinkedBlockingQueue<QueueEntry>();
|
||||
this.threadPerTask = true;
|
||||
QueueConsumer consumer = new QueueConsumer(queue, client);
|
||||
executor = getExecutor();
|
||||
executor.execute(consumer);
|
||||
}
|
||||
|
||||
private TaskExecutor getExecutor() {
|
||||
return threadPerTask ? new ThreadPerTaskExecutor() : new SingleThreadExecutor();
|
||||
}
|
||||
|
||||
public void callAsync(WebServiceCallSpec callSpec, WebServiceRequest request,
|
||||
ResponseCallback responseCallback) {
|
||||
try {
|
||||
queue.put(new QueueEntry(callSpec, request, responseCallback));
|
||||
} catch (InterruptedException e) {
|
||||
throw new WebServiceSystemException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdownNow() {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user