Last active
December 30, 2019 15:42
-
-
Save jcubic/7887543 to your computer and use it in GitHub Desktop.
Reflection based JSON-RPC Servlet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You need
jsonrpc2-base-1.35.jar
andjson-smart-1.2.jar
files in lib directory and have them in your $CLASSPATH environment variable.