Skip to content

Instantly share code, notes, and snippets.

@jcubic
Last active December 30, 2019 15:42
Show Gist options
  • Save jcubic/7887543 to your computer and use it in GitHub Desktop.
Save jcubic/7887543 to your computer and use it in GitHub Desktop.
Reflection based JSON-RPC Servlet
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.lang.reflect.*;
import com.thetransactioncompany.jsonrpc2.*;
public class init extends HttpServlet {
public init() {
}
// Read Post data from the request
public String getInputData(HttpServletRequest request) throws IOException {
ServletInputStream input = request.getInputStream();
int buff;
String output = "";
while (true) {
buff = input.read();
if (buff == -1) {
break;
}
output += (char)buff;
}
return output;
}
// load Service class dynamicaly
public Object loadService() throws Exception {
ClassLoader parentClassLoader = ServiceReloader.class.getClassLoader();
ServiceReloader classLoader = new ServiceReloader(parentClassLoader);
Class<?> aClass = classLoader.loadClass("pl.jcubic.Service");
return aClass.getConstructor(String.class).newInstance(cwd());
}
public JSONRPC2Response internalError(Object request_id, String message) {
return internalError(request_id, message, null);
}
public JSONRPC2Response internalError(Object request_id, String message, String backtrace) {
HashMap<String, String> data = new HashMap<String, String>();
data.put("exception", message);
if (backtrace != null) {
data.put("backtrace", backtrace);
}
JSONRPC2Error err = new JSONRPC2Error(-32603, "Internal Server Error", data);
return new JSONRPC2Response(err, request_id);
}
// display backtrace of the exception
public String backTrace(Throwable e) {
StackTraceElement[] stack = e.getStackTrace();
String trace = "";
for (int i=0; i<stack.length; ++i) {
trace += stack[i].toString() + "\n";
}
return trace;
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("application/json");
PrintWriter out = response.getWriter();
out.println("{\"error\": \"You need to Post JSON-RPC request!\"}");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("application/json");
PrintWriter out = response.getWriter();
Object id = null;
try {
JSONRPC2Request reqIn = JSONRPC2Request.parse(this.getInputData(request));
id = reqIn.getID();
Object[] params = reqIn.getPositionalParams().toArray();
String method_name = reqIn.getMethod();
//Service service = new Service();
Object service = loadService();
Class<?> aClass = service.getClass();
Method[] methods = aClass.getMethods();
Method method = null;
// getMethod need class as second argument to get the right method
for (int i=0; i<methods.length; ++i) {
if (methods[i].getName().equals(method_name)) {
method = methods[i];
break;
}
}
if (method != null) {
Object result = method.invoke(service, params);
JSONRPC2Response respOut = new JSONRPC2Response(result, id);
out.println(respOut);
} else {
out.println(new JSONRPC2Response(JSONRPC2Error.METHOD_NOT_FOUND, id));
}
// close the service - like destructor
Method close = aClass.getDeclaredMethod("close");
close.invoke(service);
} catch (IllegalArgumentException e) {
out.println(new JSONRPC2Response(JSONRPC2Error.INVALID_PARAMS, id));
} catch (InstantiationException e) {
out.println(internalError(id, e.getMessage()));
} catch (ClassNotFoundException e) {
out.println(internalError(id, e.getMessage()));
} catch (IllegalAccessException e) {
out.println(internalError(id, e.getMessage()));
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
String message = cause.getClass().getName() + ": " + cause.getMessage();
out.println(internalError(id, message, backTrace(cause)));
} catch (JSONRPC2ParseException e) {
out.println("{\"error\": \"Parse Error: " + e.getMessage() + "\"}");
} catch (Exception e) {
Throwable cause = e.getCause();
out.println(internalError(id, cause.getMessage(), backTrace(e)));
}
}
// join utility
public static String join(String glue, String[] array) {
if (array.length == 0) {
return null;
} else {
String output = array[0];
if (array.length > 1) {
for (int i=1; i<array.length; ++i) {
output = output + glue + array[i];
}
}
return output;
}
}
// return current working directory
public String cwd() {
return this.getServletConfig().getServletContext().getRealPath(".");
}
// convert class name to path
public String getClassPath(String name) {
String path = join(File.separator, name.split("\\."));
return this.getServletContext().getRealPath("/WEB-INF/classes/" + path + ".class");
}
// the only way to reload a class on runtime
class ServiceReloader extends ClassLoader {
public ServiceReloader(ClassLoader parent) {
super(parent);
}
public Class loadClass(String name) throws ClassNotFoundException {
if (!"pl.jcubic.Service".equals(name)) {
return super.loadClass(name);
}
try {
String path = getClassPath(name);
DataInputStream input = new DataInputStream(new BufferedInputStream(new FileInputStream(new File(path))));
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
while (true) {
int data = input.read();
if (data == -1) {
break;
}
buffer.write(data);
}
byte[] classData = buffer.toByteArray();
return defineClass(name, classData, 0, classData.length);
} catch (IOException e) {
throw new ClassNotFoundException(e.getMessage());
}
}
}
}
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>init</servlet-name>
<servlet-class>init</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>init</servlet-name>
<url-pattern>/rpc</url-pattern>
</servlet-mapping>
</web-app>
@jcubic
Copy link
Author

jcubic commented Dec 10, 2013

You need jsonrpc2-base-1.35.jar and json-smart-1.2.jar files in lib directory and have them in your $CLASSPATH environment variable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment