blob: a219de7a29085857a56a230c048ed9fd8f247dc2 [file] [log] [blame]
// Copyright 2011 Google Inc. All Rights Reserved.
//
// 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 adaptorlib;
import com.sun.net.httpserver.HttpExchange;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.nio.charset.Charset;
import java.util.*;
/**
* JSON-RPC handler for communication with the dashboard.
*/
class RpcHandler extends AbstractHandler {
private final Charset charset = Charset.forName("UTF-8");
private final GsaCommunicationHandler commHandler;
private final Map<String, RpcMethod> methods
= new HashMap<String, RpcMethod>();
public RpcHandler(String defaultHostname, Charset defaultCharset,
GsaCommunicationHandler commHandler) {
super(defaultHostname, defaultCharset);
this.commHandler = commHandler;
}
/**
* Register new RPC method.
*
* @throws IllegalStateException if method by that name already registered
*/
public void registerRpcMethod(String name, RpcMethod method) {
if (methods.containsKey(name)) {
throw new IllegalStateException("Method by that name already registered");
}
methods.put(name, method);
}
/**
* Unregister a previously registered RPC method.
*
* @throws RuntimeException if method by that name not previously registered
*/
public void unregisterRpcMethod(String name) {
if (!methods.containsKey(name)) {
throw new RuntimeException("No method by that name registered");
}
methods.remove(name);
}
@Override
public void meteredHandle(HttpExchange ex) throws IOException {
if (!"POST".equals(ex.getRequestMethod())) {
cannedRespond(ex, HttpURLConnection.HTTP_BAD_METHOD, "text/plain",
"Unsupported request method");
return;
}
if (!ex.getRequestURI().getPath().equals(ex.getHttpContext().getPath())) {
cannedRespond(ex, HttpURLConnection.HTTP_NOT_FOUND, "text/plain",
"Not found");
return;
}
Object requestObj;
{
byte[] request = IOHelper.readInputStreamToByteArray(ex.getRequestBody());
requestObj = JSONValue.parse(new String(request, charset));
}
if (requestObj == null) {
cannedRespond(ex, HttpURLConnection.HTTP_BAD_REQUEST, "text/plain",
"Could not parse JSON");
return;
}
String method;
List params;
Object id;
try {
Map request = (Map) requestObj;
method = (String) request.get("method");
params = (List) request.get("params");
id = request.get("id");
} catch (ClassCastException e) {
@SuppressWarnings("unchecked")
Map<String, Object> response = new JSONObject();
response.put("id", null);
response.put("result", null);
response.put("error", "Invalid request format: " + e.getMessage());
cannedRespond(ex, HttpURLConnection.HTTP_OK, "application/json",
JSONValue.toJSONString(response));
return;
}
// You must set one and only one of result and error.
Object result = null;
Object error = null;
try {
RpcMethod methodObj = methods.get(method);
if (methodObj != null) {
result = methodObj.run(params);
if (result == null) {
error = "Null response from method";
}
} else {
error = "Unknown method";
}
} catch (Exception e) {
error = e.getMessage();
if (error == null) {
error = "Unknown exception";
}
}
@SuppressWarnings("unchecked")
Map<String, Object> response = new JSONObject();
response.put("id", id);
response.put("result", result);
response.put("error", error);
enableCompressionIfSupported(ex);
respond(ex, HttpURLConnection.HTTP_OK, "application/json",
response.toString().getBytes(charset));
}
public interface RpcMethod {
/**
* Execute expected task for the class. Should not return {@code null}, as
* that can't be disambiguated from a misformed response. If an exception
* is thrown, the message will be sent in the response as the error.
*
* @throws Exception when something goes wrong
*/
public Object run(List request) throws Exception;
}
}