package org.appwork.remotecall.server;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.HashMap;
import org.appwork.remotecall.Utils;
import org.appwork.storage.JSonStorage;
import org.appwork.utils.Exceptions;
import org.appwork.utils.logging.Log;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
public class RemoteCallServer {
private final HashMap<String, RemoteCallServiceWrapper> servicesMap;
public RemoteCallServer() {
this.servicesMap = new HashMap<String, RemoteCallServiceWrapper>();
}
public <T> void addHandler(final Class<T> class1, final T serviceImpl) {
if (this.servicesMap.containsKey(class1.getSimpleName())) { throw new IllegalArgumentException("Service " + class1 + " already exists"); }
this.servicesMap.put(class1.getSimpleName(), RemoteCallServiceWrapper.create(serviceImpl));
}
/**
* breaks the stacktrace. the invoke process is not important
*
* @param e
* @param callerid
*/
private void cleanUpStackTrace(Throwable e, final String callerid) {
// if e is no runtimeexception, we certainly expect and handle this
// exception type anyway. no reason for transmitting stacktraces
if (e instanceof RuntimeException && !(e instanceof BadRequestException)) {
final StackTraceElement[] stack = e.getStackTrace();
StackTraceElement[] newStack = new StackTraceElement[] {};
for (final StackTraceElement el : stack) {
if (el.getClassName().startsWith(java.lang.reflect.Method.class.getName())) {
final StackTraceElement[] n = new StackTraceElement[newStack.length - 2];
System.arraycopy(newStack, 0, n, 0, newStack.length - 2);
n[n.length - 1] = new StackTraceElement("RemotecallServer from ", callerid, null, -1);
e.setStackTrace(n);
break;
} else {
final StackTraceElement[] n = new StackTraceElement[newStack.length + 1];
System.arraycopy(newStack, 0, n, 0, newStack.length);
n[n.length - 1] = el;
newStack = n;
}
}
e = e.getCause();
}
while (e != null) {
// do not send cause stacktraces
e.setStackTrace(e.getStackTrace().length > 0 ? new StackTraceElement[] { e.getStackTrace()[0] } : new StackTraceElement[] {});
e = e.getCause();
}
}
/**
* @param string
* @param class1
* @return
* @throws IOException
* @throws JsonMappingException
* @throws JsonParseException
*/
private Object convert(final String string, final Class<?> class1) throws JsonParseException, JsonMappingException, IOException {
final Object obj = JSonStorage.restoreFromString(string, class1);
return Utils.convert(obj, class1);
}
protected String handleRequest(final String remoteID, final String clazz, final String method, final String[] parameters) throws ServerInvokationException {
final RemoteCallServiceWrapper service = this.servicesMap.get(new String(clazz));
if (service == null) { throw new ServerInvokationException(this.handleRequestError(remoteID, new BadRequestException("Service not defined: " + clazz)), remoteID);
}
// find method
final Method m = service.getMethod(method);
if (m == null) { throw new ServerInvokationException(this.handleRequestError(remoteID, new BadRequestException("Routine not defined: " + method)), remoteID); }
final Class<?>[] types = m.getParameterTypes();
if (types.length != parameters.length) { throw new ServerInvokationException(this.handleRequestError(remoteID, new BadRequestException("parameters did not match " + method)), remoteID); }
final Object[] params = new Object[types.length];
try {
for (int i = 0; i < types.length; i++) {
params[i] = this.convert(URLDecoder.decode(parameters[i], "UTF-8"), types[i]);
}
} catch (final Exception e) {
throw new ServerInvokationException(this.handleRequestError(remoteID, new BadRequestException("Parameter deserialize error for " + method)), remoteID);
}
try {
Object answer;
answer = service.call(m, params);
return JSonStorage.serializeToJson(answer);
} catch (final InvocationTargetException e1) {
// TODO Auto-generated catch block
final Throwable cause = e1.getCause();
if (cause != null) { throw new ServerInvokationException(this.handleRequestError(remoteID, cause), remoteID); }
throw new ServerInvokationException(this.handleRequestError(remoteID, new RuntimeException(e1)), remoteID);
} catch (final Throwable e) {
// TODO Auto-generated catch block
throw new ServerInvokationException(this.handleRequestError(remoteID, e), remoteID);
}
}
protected String handleRequestError(final String callerid, final Throwable e) {
// TODO byte[]-generated method stub
Log.exception(e);
final StringBuilder sb = new StringBuilder();
this.cleanUpStackTrace(e, callerid);
try {
sb.append(JSonStorage.serializeToJson(new ExceptionWrapper(e)));
} catch (final Throwable e1) {
try {
sb.append(JSonStorage.serializeToJson(new ExceptionWrapper(new UnserialisableException(Exceptions.getStackTrace(e)))));
} catch (final Throwable e2) {
// TODO Auto-generated catch block
// TODO: URLENCODE here
e2.printStackTrace();
sb.append("{\"name\":\"java.lang.Exception\",\"exception\":{\"cause\":null,\"message\":\"Serialize Problem: ");
sb.append(e1.getMessage());
sb.append(e1.getLocalizedMessage());
sb.append("\",\"stackTrace\":[]}}");
}
// e1.printStackTrace();
// seems we could not serialize the original Exception. create a
// dummy
}
return sb.toString();
}
}