package knorxx.framework.generator.web.generator; import java.lang.reflect.Method; import java.util.UUID; import knorxx.framework.generator.single.JavaScriptResult; import knorxx.framework.generator.single.SingleFileGeneratorException; import static knorxx.framework.generator.util.JavaIdentifierUtils.hasSuperclassOrImplementsInterface; import knorxx.framework.generator.web.client.ErrorHandler; import knorxx.framework.generator.web.client.JsonHelper; import knorxx.framework.generator.web.client.RpcService; import knorxx.framework.generator.web.generator.stjs.StjsJavaScriptClassBuilder; import static knorxx.framework.generator.web.server.rpc.RpcCall.*; import knorxx.framework.generator.web.server.rpc.RpcCaller; import knorxx.framework.generator.web.server.rpc.RpcResult; import knorxx.framework.generator.web.server.rpc.VerboseExceptionMarshaller; /** * * @author sj */ public class RpcServiceFileGenerator extends SpecialFileGenerator { private final String csrfProtectionCookiePath; private final String rpcUrl; private final String csrfProtectionToken; public RpcServiceFileGenerator(String csrfProtectionCookiePath, String rpcUrl) { this.csrfProtectionCookiePath = csrfProtectionCookiePath; this.rpcUrl = rpcUrl; // TODO Replace that with a cryptographically more secure session id generation method. this.csrfProtectionToken = UUID.randomUUID().toString().replace("-", "").toUpperCase(); } @Override public JavaScriptResult generate(Class<?> javaClass) throws SingleFileGeneratorException { StjsJavaScriptClassBuilder builder = new StjsJavaScriptClassBuilder(javaClass) .constructor(javaClass) ._constructor(); for(Method method : javaClass.getDeclaredMethods()) { builder .function(javaClass, method) .code("document.cookie = '%s=%s; path=%s'", RpcCaller.CSRF_PROTECTION_COOKIE_NAME, csrfProtectionToken, csrfProtectionCookiePath).newLine() .code("var args = Array.prototype.slice.call(arguments);").newLine() .code("var scope = args.pop();").newLine() .code("var callback = args.pop();").newLine() .newLine() .jQueryPost() .property("url").literal(rpcUrl).comma().newLine() .property("data") .noIndent().jsonStringifyObjectLiteral()._noIndent() .newLine() .property(SERVICE_NAME_PROPERTY).literal(javaClass.getName()).comma().newLine() .property(METHOD_NAME_PROPERTY).literal(method.getName()).comma().newLine() .property(CSRF_PROTECTION_TOKEN_PROPERTY).literal(csrfProtectionToken).comma().newLine() .property(ARGUMENTS_PROPERTY).noIndent().code("args")._noIndent().newLine() ._jsonStringifyObjectLiteral().comma().newLine() .property("success") .anonymousFunction("jsonData") .code("var data = JSON.parse(jsonData, %s);", JsonHelper.PARSE_REVIVER_FUNCTION).newLine() .if$("data.status === '" + RpcResult.Status.EXCEPTION.name() + "'") .code("%s.displayRuntimeError(data.%s.%s, data.%s.%s, data.%s.%s, data.%s.%s);", ErrorHandler.INSTANCE_NAME, RpcResult.JSON_RESULT_PROPERTY, VerboseExceptionMarshaller.MESSAGE_PROPERTY, RpcResult.JSON_RESULT_PROPERTY, VerboseExceptionMarshaller.NAME_PROPERTY, RpcResult.JSON_RESULT_PROPERTY, VerboseExceptionMarshaller.LINE_NUMER_PROPERTY, RpcResult.JSON_RESULT_PROPERTY, VerboseExceptionMarshaller.STACK_TRACE_PROPERTY).newLine() .else$() .code("callback.call(scope, data.%s);", RpcResult.JSON_RESULT_PROPERTY).newLine() ._if() ._function() ._jQueryPost() ._function(); } return new JavaScriptResult(builder.create()); } @Override public boolean isGeneratable(Class<?> javaClass) { return !javaClass.getName().equals(RpcService.class.getName()) && hasSuperclassOrImplementsInterface(javaClass, RpcService.class.getName()); } }