Esempio n. 1
0
 public ReachableTypes getCache(final GeneratorContext context) {
   final String moduleName = RebindUtils.getModuleName(context);
   if (isCacheValid(context, moduleName)) {
     return moduleToReachableTypes.get(moduleName);
   } else {
     return null;
   }
 }
Esempio n. 2
0
  /**
   * Returns all bindable types on the classpath.
   *
   * @param context the current generator context
   * @return a set of meta classes representing the all bindable types (both annotated and
   *     configured in ErraiApp.properties).
   */
  public static Set<MetaClass> getAllBindableTypes(final GeneratorContext context) {
    Collection<MetaClass> annotatedBindableTypes =
        ClassScanner.getTypesAnnotatedWith(
            Bindable.class, RebindUtils.findTranslatablePackages(context), context);

    Set<MetaClass> bindableTypes = new HashSet<MetaClass>(annotatedBindableTypes);
    bindableTypes.addAll(DataBindingUtil.getConfiguredBindableTypes());
    return bindableTypes;
  }
Esempio n. 3
0
    public void putCache(final GeneratorContext context, final ReachableTypes reachableTypes) {
      if (_lastContext == null) {
        _lastContext = context;
      } else if (_lastContext != context) {
        _lastContext = context;
        moduleToReachableTypes.clear();
      }

      moduleToReachableTypes.put(RebindUtils.getModuleName(context), reachableTypes);
    }
Esempio n. 4
0
  /**
   * Erases the {@link MetaClassFactory} cache, then populates it with types discovered via GWT's
   * TypeOracle. The reason for the initial flush of the MetaClassFactory is to support hot redeploy
   * in Dev Mode. The reason for doing this operation at all is so that the overridden class
   * definitions (super-source classes) are used in preference to the Java reflection based class
   * definitions.
   *
   * @param context The GeneratorContext supplied by the GWT compiler. Not null.
   * @param logger The TreeLogger supplied by the GWT compiler. Not null.
   */
  public static void populateMetaClassFactoryFromTypeOracle(
      final GeneratorContext context, final TreeLogger logger) {

    // if we're in production mode -- it means we're compiling, and we do not need to accommodate
    // dynamically
    // changing classes. Therefore, do a NOOP after the first successful call.
    if (typeOraclePopulated && (context.equals(populatedFrom.get()) || EnvUtil.isProdMode())) {
      return;
    }

    final TypeOracle typeOracle = context.getTypeOracle();
    MetaClassFactory.emptyCache();
    if (typeOracle != null) {
      final Set<String> translatable =
          new HashSet<String>(RebindUtils.findTranslatablePackages(context));
      translatable.remove("java.lang");
      translatable.remove("java.lang.annotation");

      for (final JClassType type : typeOracle.getTypes()) {
        if (!translatable.contains(type.getPackage().getName())) {
          logger.log(
              com.google.gwt.core.ext.TreeLogger.Type.DEBUG,
              "Skipping non-translatable " + type.getQualifiedSourceName());
          continue;
        }

        if (type.isAnnotation() != null
            || type.getQualifiedSourceName().equals("java.lang.annotation.Annotation")) {
          logger.log(
              com.google.gwt.core.ext.TreeLogger.Type.DEBUG,
              "Caching annotation type " + type.getQualifiedSourceName());

          if (!MetaClassFactory.canLoadClass(type.getQualifiedBinaryName())) {
            throw new RuntimeException(
                "a new annotation has been introduced ("
                    + type.getQualifiedSourceName()
                    + "); "
                    + "you cannot currently introduce new annotations in devmode. Please restart.");
          }

          MetaClassFactory.pushCache(
              JavaReflectionClass.newUncachedInstance(
                  MetaClassFactory.loadClass(type.getQualifiedBinaryName())));
        } else {
          logger.log(
              com.google.gwt.core.ext.TreeLogger.Type.DEBUG,
              "Caching translatable type " + type.getQualifiedSourceName());
          MetaClassFactory.pushCache(GWTClass.newInstance(typeOracle, type));
        }
      }
    }
    typeOraclePopulated = true;
    populatedFrom = new SoftReference<GeneratorContext>(context);
  }
  /**
   * Renders the page-to-page navigation graph into the file {@code navgraph.gv} in the {@code
   * .errai} cache directory.
   */
  private void renderNavigationToDotFile(BiMap<String, MetaClass> pages) {
    final File dotFile = new File(RebindUtils.getErraiCacheDir().getAbsolutePath(), "navgraph.gv");
    PrintWriter out = null;
    try {
      out = new PrintWriter(dotFile);
      out.println("digraph Navigation {");
      final MetaClass transitionToType = MetaClassFactory.get(TransitionTo.class);
      final MetaClass transitionAnchorType = MetaClassFactory.get(TransitionAnchor.class);
      final MetaClass transitionAnchorFactoryType =
          MetaClassFactory.get(TransitionAnchorFactory.class);
      for (Map.Entry<String, MetaClass> entry : pages.entrySet()) {
        String pageName = entry.getKey();
        MetaClass pageClass = entry.getValue();

        // entry for the node itself
        out.print("\"" + pageName + "\"");

        Page pageAnnotation = pageClass.getAnnotation(Page.class);
        List<Class<? extends PageRole>> roles = Arrays.asList(pageAnnotation.role());
        if (roles.contains(DefaultPage.class)) {
          out.print(" [penwidth=3]");
        }
        out.println();

        for (MetaField field : getAllFields(pageClass)) {
          if (field.getType().getErased().equals(transitionToType)
              || field.getType().getErased().equals(transitionAnchorType)
              || field.getType().getErased().equals(transitionAnchorFactoryType)) {
            MetaType targetPageType = field.getType().getParameterizedType().getTypeParameters()[0];
            String targetPageName = pages.inverse().get(targetPageType);

            // entry for the link between nodes
            out.println(
                "\""
                    + pageName
                    + "\" -> \""
                    + targetPageName
                    + "\" [label=\""
                    + field.getName()
                    + "\"]");
          }
        }
      }
      out.println("}");
    } catch (FileNotFoundException e) {
      throw new RuntimeException(e);
    } finally {
      if (out != null) {
        out.close();
      }
    }
  }
  public String generate(String packageName, String clazzName) {
    File fileCacheDir = RebindUtils.getErraiCacheDir();
    File cacheFile = new File(fileCacheDir.getAbsolutePath() + "/" + clazzName + ".java");

    final Set<Class<? extends Annotation>> annos = new HashSet<Class<? extends Annotation>>();
    annos.add(Portable.class);
    annos.add(ExposeEntity.class);

    String gen;
    if (RebindUtils.hasClasspathChangedForAnnotatedWith(annos) || !cacheFile.exists()) {
      log.info("generating marshalling class...");
      long st = System.currentTimeMillis();
      gen = _generate(packageName, clazzName);
      log.info("generated marshalling class in " + (System.currentTimeMillis() - st) + "ms");

      RebindUtils.writeStringToFile(cacheFile, gen);
    } else {
      gen = RebindUtils.readFileToString(cacheFile);
      log.info("nothing has changed. using cached marshaller factory class.");
    }

    return gen;
  }
Esempio n. 7
0
  public static Class<? extends MarshallerFactory> getGeneratedMarshallerFactoryForServer() {
    final String packageName = MarshallersGenerator.SERVER_MARSHALLER_PACKAGE_NAME;
    final String className = MarshallersGenerator.SERVER_MARSHALLER_CLASS_NAME;

    try {
      log.debug("searching for marshaller class: " + packageName + "." + className);

      final String classResource = packageName.replaceAll("\\.", "/") + "/" + className + ".class";
      final Set<String> locations = new HashSet<String>();

      // look for the class in every classloader we can think of. For example, current thread
      // classloading works in Jetty but not JBoss AS 7.
      locations.addAll(
          urlToFile(Thread.currentThread().getContextClassLoader().getResources(classResource)));
      locations.addAll(
          urlToFile(ServerMarshallUtil.class.getClassLoader().getResources(classResource)));
      locations.addAll(urlToFile(ClassLoader.getSystemResources(classResource)));

      File newest = null;
      for (String url : locations) {
        final File file = ClassChangeUtil.getFileIfExists(url);
        if (file != null && (newest == null || file.lastModified() > newest.lastModified())) {
          newest = file;
        }
      }

      if (locations.size() > 1) {
        log.warn(
            "*** MULTIPLE VERSIONS OF "
                + packageName
                + "."
                + className
                + " FOUND IN CLASSPATH: "
                + "Attempted to guess the newest one based on file dates. But you should clean your output directories");

        for (String loc : locations) {
          log.warn(" Ambiguous version -> " + loc);
        }
      }

      if (newest == null) {
        try {
          // maybe we're in an appserver with a VFS, so try to load anyways.
          return Thread.currentThread()
              .getContextClassLoader()
              .loadClass(packageName + "." + className)
              .asSubclass(MarshallerFactory.class);
        } catch (ClassNotFoundException e) {
          log.warn("could not locate marshaller class.");
          if (!MarshallingGenUtil.isForceStaticMarshallers()) {
            return null;
          } else {
            log.info("couldn't find marshaller class, attempting to generate ...");
          }
        }
      } else {
        return ClassChangeUtil.loadClassDefinition(
            newest.getAbsolutePath(), packageName, className);
      }
    } catch (IOException e) {
      e.printStackTrace();
      log.warn("could not read marshaller classes: " + e);
    }

    final String classStr =
        MarshallerGeneratorFactory.getFor(MarshallerOutputTarget.Java)
            .generate(packageName, className);

    final File directory =
        new File(
            RebindUtils.getTempDirectory()
                + "/errai.gen/classes/"
                + packageName.replaceAll("\\.", "/"));

    final File sourceFile =
        new File(directory.getAbsolutePath() + File.separator + className + ".java");

    try {
      if (directory.exists()) {
        for (File file : directory.listFiles()) {
          file.delete();
        }

        directory.delete();
      }

      directory.mkdirs();

      final FileOutputStream outputStream = new FileOutputStream(sourceFile);

      outputStream.write(classStr.getBytes("UTF-8"));
      outputStream.flush();
      outputStream.close();

      String compiledClassPath =
          ClassChangeUtil.compileClass(
              directory.getAbsolutePath(), packageName, className, directory.getAbsolutePath());

      return ClassChangeUtil.loadClassDefinition(compiledClassPath, packageName, className);
    } catch (IOException e) {
      throw new RuntimeException("failed to generate class ", e);
    }
  }
Esempio n. 8
0
  public static ReachableTypes getAllReachableClasses(final GeneratorContext context) {
    if (System.getProperty(SYSPROP_USE_REACHABILITY_ANALYSIS) == null
        || !Boolean.getBoolean(SYSPROP_USE_REACHABILITY_ANALYSIS)) {

      log.warn("reachability analysis disabled. errai may generate unnecessary code.");
      log.warn(
          "enable reachability analysis with -D" + SYSPROP_USE_REACHABILITY_ANALYSIS + "=true");
      return ReachableTypes.EVERYTHING_REACHABLE_INSTANCE;
    }

    ReachabilityCache cache;
    if (reachabilityCache == null || (cache = reachabilityCache.get()) == null) {
      reachabilityCache = new SoftReference<ReachabilityCache>(cache = new ReachabilityCache());
    }

    if (cache.isCacheValid(context)) {
      return cache.getCache(context);
    }

    final EnvironmentConfig config = getEnvironmentConfig();

    long time = System.currentTimeMillis();

    final Set<String> packages = new HashSet<String>();

    if (isJUnitTest()) {
      packages.addAll(RebindUtils.findTranslatablePackagesInModule(context));
    } else {
      packages.addAll(RebindUtils.getOuterTranslatablePackages(context));
    }

    class Reachability {
      private final Set<String> packages;
      private final Set<String> negativeHits = new HashSet<String>();

      Reachability(final Set<String> packages) {
        this.packages = new HashSet<String>(packages);
      }

      public boolean isReachablePackage(final String pkg) {
        if (pkg == null || packages.contains(pkg)) {
          return true;
        }
        if (negativeHits.contains(pkg)) {
          return false;
        }

        for (final String p : packages) {
          if (pkg.startsWith(p)) {
            packages.add(pkg);
            return true;
          }
        }

        negativeHits.add(pkg);
        return false;
      }
    }

    final Set<String> allDependencies =
        Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(100));
    final Collection<MetaClass> allCachedClasses = MetaClassFactory.getAllCachedClasses();
    final ClassLoader classLoader = EnvUtil.class.getClassLoader();

    final ExecutorService executor =
        Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    final Reachability reachability = new Reachability(packages);

    try {
      for (final MetaClass mc : allCachedClasses) {
        String fullyQualifiedName = mc.getFullyQualifiedName();
        int splitPoint;
        while ((splitPoint = fullyQualifiedName.lastIndexOf('$')) != -1) {
          fullyQualifiedName = fullyQualifiedName.substring(0, splitPoint);
        }

        if (mc.isPrimitive() || mc.isArray()) {
          continue;
        } else if (isReachabilityExcluded(mc.getPackageName())) {
          continue;
        } else if (!config.getExplicitTypes().contains(fullyQualifiedName)
            && !reachability.isReachablePackage(mc.getPackageName())) {
          continue;
        }

        final URL resource =
            classLoader.getResource(fullyQualifiedName.replaceAll("\\.", "/") + ".java");

        if (resource != null) {
          InputStream stream = null;
          try {
            stream = new BufferedInputStream(resource.openStream());
            final byte[] readBuffer = new byte[stream.available()];
            stream.read(readBuffer);

            if (log.isDebugEnabled()) {
              log.debug("scanning " + fullyQualifiedName + " for reachable types ...");
            }
            executor.execute(new ReachabilityRunnable(readBuffer, allDependencies));
          } catch (IOException e) {
            log.warn("could not open resource: " + resource.getFile());
          } finally {
            if (stream != null) {
              stream.close();
            }
          }
        } else {
          log.warn("source for " + fullyQualifiedName + " is missing.");
        }
      }
    } catch (Throwable e) {
      e.printStackTrace();
    }

    try {
      executor.shutdown();
      executor.awaitTermination(60, TimeUnit.MINUTES);
    } catch (InterruptedException e) {
      log.warn("the reachability analysis was interrupted", e);
      cache.putCache(context, ReachableTypes.EVERYTHING_REACHABLE_INSTANCE);
      return ReachableTypes.EVERYTHING_REACHABLE_INSTANCE;
    }

    if (log.isDebugEnabled()) {
      log.debug("*** REACHABILITY ANALYSIS (production mode: " + EnvUtil.isProdMode() + ") ***");
      for (final String s : allDependencies) {
        log.debug(" -> " + s);
      }

      time = System.currentTimeMillis() - time;

      log.debug("*** END OF REACHABILITY ANALYSIS (" + time + "ms) *** ");
    }

    final ReachableTypes reachableTypes = new ReachableTypes(allDependencies, true);
    cache.putCache(context, reachableTypes);
    return reachableTypes;
  }
Esempio n. 9
0
 public boolean isCacheValid(final GeneratorContext context) {
   return isCacheValid(context, RebindUtils.getModuleName(context));
 }