Example #1
0
  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());
    }
  }
Example #5
0
 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();
   }
 }
Example #6
0
 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("}");
  }