This is a simple project I've put together in Eclipse that uses the Google AppEngine Java SDK and JSON-RPC library to expose an 'echo' server.
These are the components/libraries needed to make it work:
The project is made up of the following files:
- JsonRpcAppEngineTestServlet.java - The main servlet class, this is what the AppEngine runs
- TestEchoServer.java - The echo server interface
- TestEchoServerImpl.java - The echo server implementation
- TestEchoClient.java - A client to connect to the echo server
The most important thing to do before starting is to put all the required libraries in the war/WEB-INF/lib directory, otherwise AppEngine will throw ClassDefNotFoundException exceptions. In addition to this, Eclipse needs to be configured with the same libraries for it's build path.
The JsonRpcAppEngineTestServlet.java code is:
package com.colorsin.jsonrpctest;
import java.io.IOException;
import javax.servlet.http.*;
import org.json.rpc.server.JsonRpcExecutor;
import org.json.rpc.server.JsonRpcServletTransport;
@SuppressWarnings("serial")
public class JsonRpcAppEngineTestServlet extends HttpServlet {
private JsonRpcExecutor executor;
@SuppressWarnings("unchecked")
public JsonRpcAppEngineTestServlet() {
executor = new JsonRpcExecutor();
TestEchoServer server = new TestEchoServerImpl();
executor.addHandler("echo", server, TestEchoServer.class);
}
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
executor.execute(new JsonRpcServletTransport(req, resp));
}
}
What this does is when the servlet is created, it instantiates a JSON-RPC executor and an instance of the echo server, then adds a handler to the executor with the echo server object as the handler. The handler is given the name 'echo', this will be needed in the client code later.
The servlet implements the doPost() method, which means it will respond to HTTP POST requests only. This is something to watch because a default AppsEngine project will create a servlet with the doGet() method only, which will cause the following exception when the client is run:
Exception in thread "main" org.json.rpc.commons.JsonRpcClientException:
unable to get data from transport
at org.json.rpc.client.JsonRpcInvoker.invoke(JsonRpcInvoker.java:91)
at org.json.rpc.client.JsonRpcInvoker.access$000(JsonRpcInvoker.java:37)
at org.json.rpc.client.JsonRpcInvoker$1.invoke(JsonRpcInvoker.java:60)
at com.sun.proxy.$Proxy0.echo(Unknown Source)
at com.colorsin.jsonrpctest.TestEchoClient.main(TestEchoClient.java:19)
Caused by: org.json.rpc.commons.JsonRpcClientException: unexpected status code returned : 405
at org.json.rpc.client.HttpJsonRpcClientTransport.post(HttpJsonRpcClientTransport.java:74)
at org.json.rpc.client.HttpJsonRpcClientTransport.call(HttpJsonRpcClientTransport.java:45)
at org.json.rpc.client.JsonRpcInvoker.invoke(JsonRpcInvoker.java:89)
... 4 more
The TestEchoServer.java code is:
package com.colorsin.jsonrpctest;
public interface TestEchoServer {
String echo(String message);
}
This interface defines the methods that the echo 'server' exposes. In this case it's a single method that takes in a String and returns another String.
The code TestEchoServerImpl.java is:
package com.colorsin.jsonrpctest;
import java.util.Date;
public class TestEchoServerImpl implements TestEchoServer {
@Override
public String echo(String message) {
return (new Date()).toString() + ": " + message;
}
}
This class implements the TestEchoServer interface. The implementation takes the message parameter that is passed into the method and returns a String with the current date prepended to the message.
The code TestEchoClient.java is:
package com.colorsin.jsonrpctest;
import java.net.MalformedURLException;
import java.net.URL;
import org.json.rpc.client.HttpJsonRpcClientTransport;
import org.json.rpc.client.JsonRpcInvoker;
public class TestEchoClient {
@SuppressWarnings("unchecked")
public static void main(String[] args) throws MalformedURLException {
String url = "http://localhost:8888/jsonrpcappenginetest";
HttpJsonRpcClientTransport transport = new HttpJsonRpcClientTransport(new URL(url));
JsonRpcInvoker invoker = new JsonRpcInvoker();
TestEchoServer server = invoker.get(transport, "echo", TestEchoServer.class);
System.out.println(server.echo("Test message"));
}
}
The client code creates a HTTP JSON transport with the URL of the server. This URL is just the localhost (since I'm running this locally) with the servlet mapping URL pattern appended. This comes from the web.xml file in the project:
...
<servlet-mapping>
<servlet-name>JsonRpcAppEngineTest</servlet-name>
<url-pattern>/jsonrpcappenginetest</url-pattern>
</servlet-mapping>
...
Once the client has created the transport, it creates an invoker and generates a proxy object that implements the TestEchoServer interface also. This makes it seem like methods are being called on a local object, whereas in reality they are going over the network to the server that's been created.
Finally the client calls the 'server' object echo() method with a string "Test message".
Running the client produces the following:
Thu Mar 07 09:04:28 EST 2013: Test message
Adding new functionality to the server is as simple as adding a new method to the interface and implementing that method in the impl class!
-i