protected void generateProxyMethods( SourceWriter w, SerializableTypeOracle serializableTypeOracle, TypeOracle typeOracle, Map<JMethod, JMethod> syncMethToAsyncMethMap) { JMethod[] syncMethods = serviceIntf.getOverridableMethods(); for (JMethod syncMethod : syncMethods) { JMethod asyncMethod = syncMethToAsyncMethMap.get(syncMethod); assert (asyncMethod != null); JClassType enclosingType = syncMethod.getEnclosingType(); JParameterizedType isParameterizedType = enclosingType.isParameterized(); if (isParameterizedType != null) { JMethod[] methods = isParameterizedType.getMethods(); for (int i = 0; i < methods.length; ++i) { if (methods[i] == syncMethod) { /* * Use the generic version of the method to ensure that the server * can find the method using the erasure of the generic signature. */ syncMethod = isParameterizedType.getBaseType().getMethods()[i]; } } } generateProxyMethod(w, serializableTypeOracle, typeOracle, syncMethod, asyncMethod); } }
/** Examine a RequestContext subtype to populate a ContextMethod. */ private void buildContextMethod(ContextMethod.Builder contextBuilder, JClassType contextType) throws UnableToCompleteException { Service serviceAnnotation = contextType.getAnnotation(Service.class); ServiceName serviceNameAnnotation = contextType.getAnnotation(ServiceName.class); JsonRpcService jsonRpcAnnotation = contextType.getAnnotation(JsonRpcService.class); if (serviceAnnotation == null && serviceNameAnnotation == null && jsonRpcAnnotation == null) { poison( "RequestContext subtype %s is missing a @%s or @%s annotation", contextType.getQualifiedSourceName(), Service.class.getSimpleName(), JsonRpcService.class.getSimpleName()); return; } List<RequestMethod> requestMethods = new ArrayList<RequestMethod>(); for (JMethod method : contextType.getInheritableMethods()) { if (method.getEnclosingType().equals(requestContextInterface)) { // Ignore methods declared in RequestContext continue; } RequestMethod.Builder methodBuilder = new RequestMethod.Builder(); methodBuilder.setDeclarationMethod(contextType, method); if (!validateContextMethodAndSetDataType(methodBuilder, method, jsonRpcAnnotation != null)) { continue; } requestMethods.add(methodBuilder.build()); } contextBuilder .setExtraTypes(checkExtraTypes(contextType, true)) .setRequestMethods(requestMethods); }
private <T extends Annotation> T findAnnotationOnMethodOrEnclosingType( final JMethod method, final Class<T> annotationType) { T annotation = method.getAnnotation(annotationType); if (annotation == null) { annotation = method.getEnclosingType().getAnnotation(annotationType); } return annotation; }
public RequestFactoryModel(TreeLogger logger, JClassType factoryType) throws UnableToCompleteException { this.logger = logger; this.factoryType = factoryType; this.oracle = factoryType.getOracle(); collectionInterface = oracle.findType(Collection.class.getCanonicalName()); entityProxyInterface = oracle.findType(EntityProxy.class.getCanonicalName()); instanceRequestInterface = oracle.findType(InstanceRequest.class.getCanonicalName()); listInterface = oracle.findType(List.class.getCanonicalName()); mapInterface = oracle.findType(Map.class.getCanonicalName()); requestContextInterface = oracle.findType(RequestContext.class.getCanonicalName()); requestFactoryInterface = oracle.findType(RequestFactory.class.getCanonicalName()); requestInterface = oracle.findType(Request.class.getCanonicalName()); setInterface = oracle.findType(Set.class.getCanonicalName()); splittableType = oracle.findType(Splittable.class.getCanonicalName()); valueProxyInterface = oracle.findType(ValueProxy.class.getCanonicalName()); extraTypes = checkExtraTypes(factoryType, false); for (JMethod method : factoryType.getOverridableMethods()) { if (method.getEnclosingType().equals(requestFactoryInterface)) { // Ignore methods defined an RequestFactory itself continue; } if (method.getParameters().length > 0) { poison("Unexpected parameter on method %s", method.getName()); continue; } JClassType contextType = method.getReturnType().isInterface(); if (contextType == null || !requestContextInterface.isAssignableFrom(contextType)) { poison( "Unexpected return type %s on method %s is not" + " an interface assignable to %s", method.getReturnType().getQualifiedSourceName(), method.getName(), requestContextInterface.getSimpleSourceName()); continue; } ContextMethod.Builder builder = new ContextMethod.Builder(); builder.setDeclaredMethod(method); buildContextMethod(builder, contextType); contextMethods.add(builder.build()); } if (poisoned) { die(poisonedMessage()); } }
public static void checkReturnType(TreeLogger logger, JMethod method) throws UnableToCompleteException { if (!method.getReturnType().getQualifiedSourceName().equals("void")) { logger.log( Type.ERROR, "The method " + method.getEnclosingType().getQualifiedSourceName() + "." + method.getName() + " returns " + method.getReturnType().getQualifiedSourceName() + " but only void is supported for methods in RPC interfaces."); throw new UnableToCompleteException(); } }
private void implementException(TreeLogger logger, MethodBuffer mb, JMethod method) { logger.log( Type.WARN, "Unable to implement model method for " + method.getJsniSignature() + "; " + "inserting a runtime exception." + " Either annotate the method with an xapi.annotation.model, " + " or ensure it conforms to javabean naming conventions get___, set___"); mb.println("throw new RuntimeException(\""); mb.println( "Model method " + method.getEnclosingType().getSimpleSourceName() + "." + method.getName() + " not annotated correctly"); mb.println("\");"); }
/** Simply prints a JSNI reference to the exported function. */ private static void writeIdentityInvocation(FragmentGeneratorContext context, JMethod m) throws UnableToCompleteException { SourceWriter sw = context.sw; JParameter[] parameters = m.getParameters(); sw.print("@"); sw.print(m.getEnclosingType().getQualifiedSourceName()); sw.print("::"); sw.print(m.getName()); sw.print("("); // Argument list for the Java invocation for (int i = 0; i < parameters.length; i++) { sw.print(parameters[i].getType().getJNISignature()); } sw.print(")"); }
protected boolean mustDelegateToController(JMethod method) { JClassType enclosingType = method.getEnclosingType(); return (!enclosingType.equals(deviceAdaptiveClass) && !enclosingType.equals(hasHandlersClass)); }
private EntityProxyModel getEntityProxyType(JClassType entityProxyType) throws UnableToCompleteException { entityProxyType = ModelUtils.ensureBaseType(entityProxyType); EntityProxyModel toReturn = peers.get(entityProxyType); if (toReturn == null) { EntityProxyModel.Builder inProgress = peerBuilders.get(entityProxyType); if (inProgress != null) { toReturn = inProgress.peek(); } } if (toReturn == null) { EntityProxyModel.Builder builder = new EntityProxyModel.Builder(); peerBuilders.put(entityProxyType, builder); // Validate possible super-proxy types first for (JClassType supertype : entityProxyType.getFlattenedSupertypeHierarchy()) { List<EntityProxyModel> superTypes = new ArrayList<EntityProxyModel>(); if (supertype != entityProxyType && shouldAttemptProxyValidation(supertype)) { superTypes.add(getEntityProxyType(supertype)); } builder.setSuperProxyTypes(superTypes); } builder.setQualifiedBinaryName(ModelUtils.getQualifiedBaseBinaryName(entityProxyType)); builder.setQualifiedSourceName(ModelUtils.getQualifiedBaseSourceName(entityProxyType)); if (entityProxyInterface.isAssignableFrom(entityProxyType)) { builder.setType(Type.ENTITY); } else if (valueProxyInterface.isAssignableFrom(entityProxyType)) { builder.setType(Type.VALUE); } else { poison( "The type %s is not assignable to either %s or %s", entityProxyInterface.getQualifiedSourceName(), valueProxyInterface.getQualifiedSourceName()); // Cannot continue, since knowing the behavior is crucial die(poisonedMessage()); } // Get the server domain object type ProxyFor proxyFor = entityProxyType.getAnnotation(ProxyFor.class); ProxyForName proxyForName = entityProxyType.getAnnotation(ProxyForName.class); JsonRpcProxy jsonRpcProxy = entityProxyType.getAnnotation(JsonRpcProxy.class); if (proxyFor == null && proxyForName == null && jsonRpcProxy == null) { poison( "The %s type does not have a @%s, @%s, or @%s annotation", entityProxyType.getQualifiedSourceName(), ProxyFor.class.getSimpleName(), ProxyForName.class.getSimpleName(), JsonRpcProxy.class.getSimpleName()); } // Look at the methods declared on the EntityProxy List<RequestMethod> requestMethods = new ArrayList<RequestMethod>(); Map<String, JMethod> duplicatePropertyGetters = new HashMap<String, JMethod>(); for (JMethod method : entityProxyType.getInheritableMethods()) { if (method.getEnclosingType().equals(entityProxyInterface)) { // Ignore methods on EntityProxy continue; } RequestMethod.Builder methodBuilder = new RequestMethod.Builder(); methodBuilder.setDeclarationMethod(entityProxyType, method); JType transportedType; String name = method.getName(); if (JBeanMethod.GET.matches(method)) { transportedType = method.getReturnType(); String propertyName = JBeanMethod.GET.inferName(method); JMethod previouslySeen = duplicatePropertyGetters.get(propertyName); if (previouslySeen == null) { duplicatePropertyGetters.put(propertyName, method); } else { poison( "Duplicate accessors for property %s: %s() and %s()", propertyName, previouslySeen.getName(), method.getName()); } } else if (JBeanMethod.SET.matches(method) || JBeanMethod.SET_BUILDER.matches(method)) { transportedType = method.getParameters()[0].getType(); } else if (name.equals("stableId") && method.getParameters().length == 0) { // Ignore any overload of stableId continue; } else { poison("The method %s is neither a getter nor a setter", method.getReadableDeclaration()); continue; } validateTransportableType(methodBuilder, transportedType, false); RequestMethod requestMethod = methodBuilder.build(); requestMethods.add(requestMethod); } builder .setExtraTypes(checkExtraTypes(entityProxyType, false)) .setRequestMethods(requestMethods); toReturn = builder.build(); peers.put(entityProxyType, toReturn); peerBuilders.remove(entityProxyType); } return toReturn; }
private RequestQueueModel collectModel( TreeLogger logger, GeneratorContext context, JClassType toGenerate) throws UnableToCompleteException { RequestQueueModel.Builder rqBuilder = new RequestQueueModel.Builder(); TypeOracle typeOracle = context.getTypeOracle(); JClassType requestQueue = typeOracle.findType(RequestQueue.class.getName()); JClassType asyncCallback = typeOracle.findType(AsyncCallback.class.getName()); JClassType voidType = typeOracle.findType(Void.class.getName()); rqBuilder.setRequestQueueInterfaceName(toGenerate.getParameterizedQualifiedSourceName()); AsyncServiceModel.Builder serviceBuilder = new AsyncServiceModel.Builder(); for (JMethod m : toGenerate.getMethods()) { TreeLogger serviceLogger = logger.branch(Type.DEBUG, "Reading async service " + m.getReadableDeclaration()); // Skip those defined at RequestQueue if (m.getEnclosingType().equals(requestQueue)) { continue; } JClassType returnType = m.getReturnType().isClassOrInterface(); if (returnType == null) { serviceLogger.log(Type.ERROR, "Unexpected method return type " + returnType); throw new UnableToCompleteException(); } serviceBuilder.setAsyncServiceInterfaceName(returnType.getParameterizedQualifiedSourceName()); serviceBuilder.setDeclaredMethodName(m.getName()); Service serviceAnnotation = m.getAnnotation(Service.class); if (serviceAnnotation == null) { serviceLogger.log(Type.ERROR, "Missing @Service annotation"); throw new UnableToCompleteException(); } serviceBuilder.setService(serviceAnnotation.value().getName()); AsyncServiceMethodModel.Builder methodBuilder = new AsyncServiceMethodModel.Builder(); for (JMethod asyncMethod : m.getReturnType().isClassOrInterface().getMethods()) { TreeLogger methodLogger = serviceLogger.branch( Type.DEBUG, "Reading service method " + asyncMethod.getReadableDeclaration()); List<JType> types = new ArrayList<JType>(); methodBuilder.setReturnTypeName(voidType.getParameterizedQualifiedSourceName()); boolean asyncFound = false; for (JType param : asyncMethod.getParameterTypes()) { if (asyncFound) { methodLogger.log( Type.WARN, "Already passed an AsyncCallback param - is that what you meant?"); } if (param.isClassOrInterface() != null && param.isClassOrInterface().isAssignableTo(asyncCallback)) { JClassType boxedReturnType = ModelUtils.findParameterizationOf(asyncCallback, param.isClassOrInterface())[0]; methodBuilder .setHasCallback(true) .setReturnTypeName(boxedReturnType.getParameterizedQualifiedSourceName()); asyncFound = true; continue; // should be last, check for this... } types.add(param); } Set<JClassType> throwables = new HashSet<JClassType>(); Throws t = asyncMethod.getAnnotation(Throws.class); if (t != null) { for (Class<? extends Throwable> throwable : t.value()) { throwables.add(typeOracle.findType(throwable.getName())); } } methodBuilder .setMethodName(asyncMethod.getName()) .setArgTypes(types) .setThrowables(throwables); serviceBuilder.addMethod(methodBuilder.build()); } rqBuilder.addService(serviceBuilder.build()); } return rqBuilder.build(); }
/** Writes a linkage function object that will invoke the exported function. */ private static void writeLinkageInvocation(FragmentGeneratorContext context, JMethod m) throws UnableToCompleteException { TreeLogger logger = context.parentLogger.branch( TreeLogger.DEBUG, "Writing function() for " + m.getName(), null); SourceWriter sw = context.sw; JParameter[] parameters = m.getParameters(); FragmentGeneratorOracle fgo = context.fragmentGeneratorOracle; FragmentGenerator returnFragmentGenerator = fgo.findFragmentGenerator(logger, context.typeOracle, m.getReturnType()); sw.print("function("); for (int i = 0; i < parameters.length; i++) { sw.print("arg"); sw.print(String.valueOf(i)); if (i < parameters.length - 1) { sw.print(", "); } } sw.println(") {"); sw.indent(); if (returnFragmentGenerator.isIdentity()) { sw.print("return "); } else { sw.print("var javaReturn = "); } // Don't need to reference the instance on a static method if (!m.isStatic()) { sw.print(context.parameterName); sw.print("."); } sw.print("@"); sw.print(m.getEnclosingType().getQualifiedSourceName()); sw.print("::"); sw.print(m.getName()); sw.print("("); // Argument list for the Java invocation for (int i = 0; i < parameters.length; i++) { sw.print(parameters[i].getType().getJNISignature()); } sw.println(")("); // Indent the parameters, each on its own like to improve readability sw.indent(); sw.indent(); for (int i = 0; i < parameters.length; i++) { // Create a sub-context to generate the wrap/unwrap logic JType returnType = parameters[i].getType(); FragmentGeneratorContext subParams = new FragmentGeneratorContext(context); subParams.returnType = returnType; subParams.parameterName = "arg" + i; FragmentGenerator fragmentGenerator = fgo.findFragmentGenerator(logger, context.typeOracle, returnType); if (fragmentGenerator == null) { logger.log( TreeLogger.ERROR, "No fragment generator for " + returnType.getQualifiedSourceName(), null); throw new UnableToCompleteException(); } fragmentGenerator.fromJS(subParams); if (i < parameters.length - 1) { sw.println(", "); } } sw.outdent(); sw.outdent(); sw.println(");"); if (!returnFragmentGenerator.isIdentity()) { FragmentGeneratorContext returnContext = new FragmentGeneratorContext(context); returnContext.parameterName = "javaReturn"; returnContext.returnType = m.getReturnType(); sw.print("return "); returnFragmentGenerator.toJS(returnContext); sw.println(";"); } sw.outdent(); sw.print("}"); }