Package org.jboss.errai.config.rebind

Source Code of org.jboss.errai.config.rebind.EnvUtil$ReachabilityRunnable

/*
* Copyright 2011 JBoss, by Red Hat, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jboss.errai.config.rebind;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import com.google.gwt.thirdparty.guava.common.io.Files;
import org.jboss.errai.codegen.meta.MetaClass;
import org.jboss.errai.codegen.meta.MetaClassFactory;
import org.jboss.errai.codegen.util.QuickDeps;
import org.jboss.errai.common.client.api.annotations.NonPortable;
import org.jboss.errai.common.client.api.annotations.Portable;
import org.jboss.errai.common.client.types.TypeHandlerFactory;
import org.jboss.errai.common.metadata.MetaDataScanner;
import org.jboss.errai.common.metadata.RebindUtils;
import org.jboss.errai.common.metadata.ScannerSingleton;
import org.jboss.errai.config.util.ClassScanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gwt.core.ext.GeneratorContext;

import javax.xml.transform.stream.StreamSource;

/**
* @author Mike Brock
*/
public abstract class EnvUtil {
  public static final String CONFIG_ERRAI_SERIALIZABLE_TYPE = "errai.marshalling.serializableTypes";
  public static final String CONFIG_ERRAI_NONSERIALIZABLE_TYPE = "errai.marshalling.nonserializableTypes";
  public static final String CONFIG_ERRAI_MAPPING_ALIASES = "errai.marshalling.mappingAliases";

  public static final String SYSPROP_USE_REACHABILITY_ANALYSIS = "errai.compile.perf.perform_reachability_analysis";

  private static final Logger logger = LoggerFactory.getLogger("Env");

  private static volatile Boolean _isJUnitTest;

  public static boolean isJUnitTest() {
    if (_isJUnitTest != null) return _isJUnitTest;

    for (final StackTraceElement el : new Throwable().getStackTrace()) {
      if (el.getClassName().startsWith("com.google.gwt.junit.client.")
          || el.getClassName().startsWith("org.junit")) {
        return _isJUnitTest = Boolean.TRUE;
      }
    }
    return _isJUnitTest = Boolean.FALSE;
  }

  private static volatile Boolean _isDevMode;

  public static boolean isDevMode() {
    if (_isDevMode != null) return _isDevMode;

    for (final StackTraceElement el : new Throwable().getStackTrace()) {
      if (el.getClassName().startsWith("com.google.gwt.dev.shell.OophmSessionHandler")) {
        return _isDevMode = Boolean.TRUE;
      }
    }
    return _isDevMode = Boolean.FALSE;
  }

  private static volatile Boolean _isProdMode;

  public static boolean isProdMode() {
    if (_isProdMode != null) return _isProdMode;

    return _isProdMode = Boolean.valueOf(!isDevMode() && !isJUnitTest());
  }

  public static void recordEnvironmentState() {
    isJUnitTest();
    isDevMode();
    isProdMode();
  }

  private static Logger log = LoggerFactory.getLogger(EnvUtil.class);

  private static EnviromentConfig loadConfiguredPortableTypes() {
    final Map<String, String> frameworkProps = new HashMap<String, String>();
    final Map<String, String> mappingAliases = new HashMap<String, String>();
    final Set<MetaClass> exposedClasses = new HashSet<MetaClass>();
    final Set<MetaClass> nonportableClasses = new HashSet<MetaClass>();
    final Set<String> explicitTypes = new HashSet<String>();
    final Set<MetaClass> portableNonExposed = new HashSet<MetaClass>();

    final Set<MetaClass> exposedFromScanner = new HashSet<MetaClass>(ClassScanner.getTypesAnnotatedWith(Portable.class));
    nonportableClasses.addAll(ClassScanner.getTypesAnnotatedWith(NonPortable.class));

    for (final MetaClass cls : exposedFromScanner) {
      for (final MetaClass decl : cls.getDeclaredClasses()) {
        if (decl.isSynthetic()) {
          continue;
        }

        exposedClasses.add(decl);
      }
    }

    exposedClasses.addAll(exposedFromScanner);

    final Enumeration<URL> erraiAppProperties;

    try {
      erraiAppProperties = Thread.currentThread().getContextClassLoader()
          .getResources("ErraiApp.properties");
    }
    catch (IOException e) {
      throw new RuntimeException("failed to load ErraiApp.properties from classloader", e);
    }

    while (erraiAppProperties.hasMoreElements()) {
      InputStream inputStream = null;
      try {
        final URL url = erraiAppProperties.nextElement();

        log.debug("checking " + url.getFile() + " for configured types ...");

        inputStream = url.openStream();

        final ResourceBundle props = new PropertyResourceBundle(inputStream);
        if (props != null) {

          for (final Object o : props.keySet()) {
            final String key = (String) o;

            frameworkProps.put(key, props.getString(key));

            if (key.equals(CONFIG_ERRAI_SERIALIZABLE_TYPE)) {
              for (final String s : props.getString(key).split(" ")) {
                try {
                  exposedClasses.add(MetaClassFactory.get(s.trim()));
                  explicitTypes.add(s.trim());
                }
                catch (Exception e) {
                  throw new RuntimeException("could not find class defined in ErraiApp.properties for serialization: " + s);
                }
              }

              continue;
            }

            if (key.equals(CONFIG_ERRAI_NONSERIALIZABLE_TYPE)) {
              for (final String s : props.getString(key).split(" ")) {
                try {
                  nonportableClasses.add(MetaClassFactory.get(s.trim()));
                }
                catch (Exception e) {
                  throw new RuntimeException("could not find class defined in ErraiApp.properties as nonserializable: " + s);
                }
              }

              continue;
            }

            if (key.equals(CONFIG_ERRAI_MAPPING_ALIASES)) {
              for (final String s : props.getString(key).split(" ")) {
                try {
                  final String[] mapping = s.split("->");

                  if (mapping.length != 2) {
                    throw new RuntimeException("syntax error: mapping for marshalling alias: " + s);
                  }

                  final Class<?> fromMapping = Class.forName(mapping[0].trim());
                  final Class<?> toMapping = Class.forName(mapping[1].trim());

                  mappingAliases.put(fromMapping.getName(), toMapping.getName());
                  explicitTypes.add(fromMapping.getName());
                  explicitTypes.add(toMapping.getName());
                }
                catch (Exception e) {
                  throw new RuntimeException("could not find class defined in ErraiApp.properties for mapping: " + s);
                }
              }
              continue;
            }
          }
        }
      }
      catch (IOException e) {
        throw new RuntimeException("error reading ErraiApp.properties", e);
      }
      finally {
        if (inputStream != null) {
          try {
            inputStream.close();
          }
          catch (IOException e) {
            //
          }
        }
      }
    }

    // must do this before filling in interfaces and supertypes!
    exposedClasses.removeAll(nonportableClasses);

    for (final MetaClass cls : exposedClasses) {
      fillInInterfacesAndSuperTypes(portableNonExposed, cls);
    }

    return new EnviromentConfig(mappingAliases, exposedClasses, portableNonExposed, explicitTypes, frameworkProps);
  }

  private static void fillInInterfacesAndSuperTypes(final Set<MetaClass> set, final MetaClass type) {
    for (final MetaClass iface : type.getInterfaces()) {
      set.add(iface);
      fillInInterfacesAndSuperTypes(set, iface);
    }
    if (type.getSuperClass() != null) {
      fillInInterfacesAndSuperTypes(set, type.getSuperClass());
    }
  }

  private static EnviromentConfig _environmentConfigCache;

  public static EnviromentConfig getEnvironmentConfig() {
    if (_environmentConfigCache == null) _environmentConfigCache = loadConfiguredPortableTypes();
    return _environmentConfigCache;
  }

  public static boolean isPortableType(final Class<?> cls) {
    final MetaClass mc = MetaClassFactory.get(cls);
    if (cls.isAnnotationPresent(Portable.class) || getEnvironmentConfig().getExposedClasses().contains(mc)
        || getEnvironmentConfig().getPortableSuperTypes().contains(mc)) {
      return true;
    }
    else {
      if (String.class.equals(cls) || TypeHandlerFactory.getHandler(cls) != null) {
        return true;
      }
    }
    return false;
  }

  public static Set<Class<?>> getAllPortableConcreteSubtypes(final Class<?> clazz) {
    final Set<Class<?>> portableSubtypes = new HashSet<Class<?>>();
    if (isPortableType(clazz)) {
      portableSubtypes.add(clazz);
    }

    for (final Class<?> subType : ScannerSingleton.getOrCreateInstance().getSubTypesOf(clazz)) {
      if (isPortableType(subType)) {
        portableSubtypes.add(subType);
      }
    }

    return portableSubtypes;
  }

  public static Set<Class<?>> getAllPortableSubtypes(final Class<?> clazz) {
    final Set<Class<?>> portableSubtypes = new HashSet<Class<?>>();
    if (clazz.isInterface() || isPortableType(clazz)) {
      portableSubtypes.add(clazz);
    }

    for (final Class<?> subType : ScannerSingleton.getOrCreateInstance().getSubTypesOf(clazz)) {
      if (clazz.isInterface() || isPortableType(subType)) {
        portableSubtypes.add(subType);
      }
    }

    return portableSubtypes;
  }

  public static void clearCaches() {
    _environmentConfigCache = null;
  }

  private static volatile GeneratorContext _lastContext;
  private static volatile ReachableTypes _reachableCache;

  public static ReachableTypes getAllReachableClasses(final GeneratorContext context) {
    if (_lastContext == context && _reachableCache != null) {
      return _reachableCache;
    }

    final EnviromentConfig config = getEnvironmentConfig();

    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;
    }

    long time = System.currentTimeMillis();

    final Set<String> packages = RebindUtils.findTranslatablePackagesInModule(context);

    final Set<String> allDeps = 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());

    try {
      for (final MetaClass mc : allCachedClasses) {
        if (!config.getExplicitTypes().contains(mc.getFullyQualifiedName())
            && !packages.contains(mc.getPackageName())) continue;

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

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

            executor.execute(new ReachabilityRunnable(readBuffer, allDeps));
          }
          catch (IOException e) {
            log.warn("could not open resource: " + resource.getFile());
          }
          finally {
            if (stream != null) {
              stream.close();
            }
          }
        }
      }
    }
    catch (Throwable e) {
      e.printStackTrace();
    }

    try {
      executor.shutdown();
      executor.awaitTermination(60, TimeUnit.MINUTES);
    }
    catch (InterruptedException e) {
      e.printStackTrace();
    }

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

      time = System.currentTimeMillis() - time;

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

    _lastContext = context;
    return _reachableCache = new ReachableTypes(allDeps, true);
  }

  private static class ReachabilityRunnable implements Runnable {
    private final byte[] sourceBuffer;
    private final Set<String> results;

    private ReachabilityRunnable(final byte[] sourceBuffer, final Set<String> results) {
      this.sourceBuffer = sourceBuffer;
      this.results = results;
    }

    @Override
    public void run() {
      results.addAll(QuickDeps.getQuickTypeDependencyList(new String(sourceBuffer), null));
    }
  }
}
TOP

Related Classes of org.jboss.errai.config.rebind.EnvUtil$ReachabilityRunnable

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.