Package com.google.gdt.eclipse.designer.hosted.tdt

Source Code of com.google.gdt.eclipse.designer.hosted.tdt.HostedModeSupport$DispatchIdOracleImpl

/*******************************************************************************
* Copyright 2011 Google Inc. All Rights Reserved.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* 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 com.google.gdt.eclipse.designer.hosted.tdt;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.MapMaker;
import com.google.gdt.eclipse.designer.hosted.HostedModeException;
import com.google.gdt.eclipse.designer.hosted.IBrowserShell;
import com.google.gdt.eclipse.designer.hosted.IBrowserShellFactory;
import com.google.gdt.eclipse.designer.hosted.IHostedModeSupport;
import com.google.gdt.eclipse.designer.hosted.ILogSupport;
import com.google.gdt.eclipse.designer.hosted.IModuleDescription;
import com.google.gdt.eclipse.designer.hosted.tdt.log.LogSupport;
import com.google.gwt.dev.shell.designtime.DispatchClassInfo;
import com.google.gwt.dev.shell.designtime.DispatchIdOracle;
import com.google.gwt.dev.shell.designtime.ModuleSpace;

import org.eclipse.wb.internal.core.EnvironmentUtils;
import org.eclipse.wb.internal.core.utils.external.ExternalFactoriesHelper;
import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.swt.widgets.Display;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.SystemUtils;
import org.osgi.framework.Bundle;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;
import java.util.Map;

/**
* Implementation for {@link IHostedModeSupport} for GWT. Also used as {@link IBrowserShellHost}
* while creating {@link ModuleSpace} for current platform.
*
* @author mitin_aa
* @coverage gwtHosted
*/
public final class HostedModeSupport implements IHostedModeSupport, IBrowserShellHost {
  private static Map<String, ClassLoader> devClassLoaders = new MapMaker().softValues().makeMap();
  private final ClassLoader parentClassLoader;
  private final IModuleDescription moduleDescription;
  private final BrowserShell browserShell;
  private final IJavaProject javaProject;
  private ClassLoader projectClassLoader;
  private Object moduleSpaceHost;
  private final LogSupport logSupport;
  private Object impl;
  private DispatchIdOracle dispatchIdOracle;

  ////////////////////////////////////////////////////////////////////////////
  //
  // Constructor
  //
  ////////////////////////////////////////////////////////////////////////////
  public HostedModeSupport(ClassLoader parentClassLoader, IModuleDescription moduleDescription)
      throws Exception {
    this.parentClassLoader = parentClassLoader;
    this.moduleDescription = moduleDescription;
    this.javaProject = moduleDescription.getJavaProject();
    // Class loaders
    createClassLoaders();
    // impl
    loadImpl();
    // Logger
    this.logSupport = new LogSupport(3 /*TreeLogger.TRACE*/, impl, javaProject);
    // Browser shell
    this.browserShell = (BrowserShell) createBrowserShell();
    this.browserShell.setHost(this);
  }

  /**
   * Constructor to use for "warm up".
   */
  public HostedModeSupport(IModuleDescription moduleDescription) throws Exception {
    this.parentClassLoader = null;
    this.moduleDescription = moduleDescription;
    this.javaProject = moduleDescription.getJavaProject();
    // Class loaders
    createClassLoaders();
    // impl
    loadImpl();
    // Logger
    this.logSupport = new LogSupport(3 /*TreeLogger.TRACE*/, impl, javaProject);
    // Browser shell
    this.browserShell = null;
  }

  private void loadImpl() throws Exception {
    Class<?> implClass =
        getDevClassLoader().loadClass("com.google.gwt.dev.shell.designtime.HostedModeSupportImpl");
    impl = implClass.newInstance();
    //
    /*Class<?> moduleSpaceClass =
        getDevClassLoader().loadClass("com.google.gwt.dev.shell.designtime.DelegatingModuleSpace");
    ModuleSpace.setDelegatingModuleSpaceClass(moduleSpaceClass);*/
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // ClassLoaders
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * Creates the special {@link ClassLoader}'s to work with GWT "dev" classes and "user" classes.
   */
  private void createClassLoaders() throws Exception {
    if (javaProject == null) {
      projectClassLoader = ClassLoader.getSystemClassLoader();
    } else {
      ClassLoader devClassLoader = getDevClassLoader0();
      projectClassLoader = new LocalProjectClassLoader(moduleDescription.getURLs(), devClassLoader);
    }
  }

  private static final class LocalProjectClassLoader extends URLClassLoader {
    private LocalProjectClassLoader(URL[] urls, ClassLoader parent) {
      super(urls, parent);
    }

    @Override
    public URL findResource(String name) {
      URL url = super.findResource(name);
      if (isWrongURL(url)) {
        url = null;
      }
      return url;
    }

    /**
     * @return <code>true</code> if given {@link URL} represents {@link File} with non-canonical
     *         path, such as using incorrect case on Windows. JDT compiler tried to detect if given
     *         name "test" is name of package or not, by searching for "test.class" resource. But on
     *         Windows file system is not case sensitive, so "Test.class" resource returned, so it
     *         is considered not as package, but as type.
     */
    private boolean isWrongURL(URL url) {
      if (EnvironmentUtils.IS_WINDOWS) {
        File file = FileUtils.toFile(url);
        if (file != null && file.exists()) {
          try {
            String absolutePath = file.getAbsolutePath();
            String canonicalPath = file.getCanonicalPath();
            return !absolutePath.equals(canonicalPath);
          } catch (Throwable e) {
          }
        }
      }
      return false;
    }
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // IHostedModeSupport
  //
  ////////////////////////////////////////////////////////////////////////////
  public void startup(String browserStartupUrl,
      String moduleName,
      IProgressMonitor monitor,
      int timeout) throws Exception {
    browserShell.setUrl(browserStartupUrl, moduleName, timeout, new Runnable() {
      public void run() {
        runMessagesLoop();
      }
    });
    // setup parent for CompilingClassLoader
    ClassLoader classLoader = getClassLoader();
    ReflectionUtils.setField(classLoader, "parent", parentClassLoader);
  }

  public void dispose() {
    if (moduleSpaceHost != null) {
      // clear static caches
      ClassLoader devClassLoader = getDevClassLoader();
      try {
        Class<?> clazz = devClassLoader.loadClass("com.google.gwt.i18n.rebind.ClearStaticData");
        ReflectionUtils.invokeMethod2(clazz, "clear");
      } catch (Throwable e) {
      }
      try {
        Class<?> clazz =
            devClassLoader.loadClass("com.google.gwt.uibinder.rebind.model.OwnerFieldClass");
        Map<?, ?> map = (Map<?, ?>) ReflectionUtils.getFieldObject(clazz, "FIELD_CLASSES");
        map.clear();
      } catch (Throwable e) {
      }
      // remove parent of CompilingClassLoader
      if (parentClassLoader != null) {
        ClassLoader classLoader = getClassLoader();
        ReflectionUtils.setField(classLoader, "parent", null);
      }
      // clear "loadedModulesCaches" in com.google.gwt.dev.cfg.ModuleDefLoader
      try {
        Class<?> moduleDefLoader =
            devClassLoader.loadClass("com.google.gwt.dev.cfg.ModuleDefLoader");
        Map<?, ?> loadedModulesCaches =
            (Map<?, ?>) ReflectionUtils.getFieldObject(moduleDefLoader, "loadedModulesCaches");
        loadedModulesCaches.clear();
      } catch (Throwable e) {
      }
      /*// clear "threadLocalLogger" in com.google.gwt.dev.shell.ModuleSpace
      try {
        Class<?> classModuleSpace =
            devClassLoader.loadClass("com.google.gwt.dev.shell.ModuleSpace");
        ThreadLocal<?> threadLocalLogger =
            (ThreadLocal<?>) ReflectionUtils.getFieldObject(classModuleSpace, "threadLocalLogger");
        threadLocalLogger.set(null);
      } catch (Throwable e) {
      }
      // shutdown com.google.gwt.dev.javac.PersistentUnitCache
      try {
        Class<?> classUnitCacheFactory =
            devClassLoader.loadClass("com.google.gwt.dev.javac.UnitCacheFactory");
        Object cacheInstance = ReflectionUtils.getFieldObject(classUnitCacheFactory, "instance");
        if (cacheInstance != null) {
          Method shutdownMethod =
              ReflectionUtils.getMethodBySignature(cacheInstance.getClass(), "shutdown()");
          if (shutdownMethod != null) {
            shutdownMethod.invoke(cacheInstance);
          }
        }
        ReflectionUtils.setField(classUnitCacheFactory, "instance", null);
      } catch (Throwable e) {
      }
      // Call and remove GWT related java.lang.ApplicationShutdownHooks
      try {
        Class<?> hooksClass =
            ClassLoader.getSystemClassLoader().loadClass("java.lang.ApplicationShutdownHooks");
        Field hooksField = ReflectionUtils.getFieldByName(hooksClass, "hooks");
        @SuppressWarnings("unchecked")
        Map<Thread, ?> hooks = (Map<Thread, ?>) hooksField.get(null);
        List<Thread> threads = ImmutableList.copyOf(hooks.keySet());
        for (Thread thread : threads) {
          ClassLoader contextClassLoader = thread.getContextClassLoader();
          if (contextClassLoader == devClassLoader) {
            thread.setContextClassLoader(ClassLoader.getSystemClassLoader());
            thread.run();
            hooks.remove(thread);
          }
        }
      } catch (Throwable e) {
        e.printStackTrace();
      }
      // close com.google.gwt.dev.util.DiskCache
      try {
        Class<?> classDiskCache = devClassLoader.loadClass("com.google.gwt.dev.util.DiskCache");
        Object cacheInstance = ReflectionUtils.getFieldObject(classDiskCache, "INSTANCE");
        ReflectionUtils.invokeMethod(cacheInstance, "close()");
      } catch (Throwable e) {
      }
      // find embedded Guava Finalizer and clear reference of our "dev" URLClassLoader
      try {
        Thread[] threads = getAllThreads();
        for (Thread thread : threads) {
          if (thread != null && thread.getContextClassLoader() == devClassLoader) {
            thread.setContextClassLoader(null);
          }
        }
      } catch (Throwable e) {
      }*/
    }
    //
    if (browserShell != null) {
      browserShell.dispose();
    }
    logSupport.dispose();
    moduleSpaceHost = null;
    impl = null;
    projectClassLoader = null;
    dispatchIdOracle = null;
  }

  /**
   * @return array of {@link Thread}s, may be with <code>null</code> on the end.
   */
  private static Thread[] getAllThreads() {
    // prepare root ThreadGroup
    ThreadGroup rootGroup = Thread.currentThread().getThreadGroup();
    ThreadGroup parentGroup;
    while ((parentGroup = rootGroup.getParent()) != null) {
      rootGroup = parentGroup;
    }
    // fill Thread array
    Thread[] threads = new Thread[rootGroup.activeCount()];
    while (rootGroup.enumerate(threads, true) == threads.length) {
      threads = new Thread[threads.length * 2];
    }
    return threads;
  }

  public IBrowserShell getBrowserShell() {
    return browserShell;
  }

  public ClassLoader getClassLoader() {
    // returns CompilingClassLoader
    try {
      return (ClassLoader) ReflectionUtils.invokeMethod2(moduleSpaceHost, "getClassLoader");
    } catch (Throwable e) {
      throw ReflectionUtils.propagate(e);
    }
  }

  public ClassLoader getDevClassLoader() {
    return projectClassLoader;
  }

  public void invalidateRebind(String typeName) {
    try {
      browserShell.getModuleSpace().invalidateRebind(typeName);
    } catch (Throwable e) {
      ReflectionUtils.propagate(e);
    }
  }

  public Object findJType(String name) {
    try {
      return ReflectionUtils.invokeMethod(impl, "findJType(java.lang.String)", name);
    } catch (Throwable e) {
      throw ReflectionUtils.propagate(e);
    }
  }

  /**
   * @return the {@link ClassLoader} for accessing gwt-dev classes mixed with design-time support
   *         lib.
   */
  private ClassLoader getDevClassLoader0() throws Exception {
    // prepare gwt-dev.jar location
    String devLibLocation = Utils.getDevLibLocation(moduleDescription);
    if (devLibLocation == null) {
      throw new HostedModeException(HostedModeException.NO_DEV_LIB);
    }
    String gwtLocation = FilenameUtils.getFullPath(devLibLocation);
    // add 'dev' & 'dev-designtime'
    ClassLoader devClassLoader = devClassLoaders.get(gwtLocation);
    if (devClassLoader == null) {
      URL resolvedDevLibUrl = new File(devLibLocation).toURI().toURL();
      Bundle bundle = Activator.getDefault().getBundle();
      URL devDesignUrl = FileLocator.resolve(bundle.getEntry("/gwt-dev-designtime.jar"));
      if (devDesignUrl != null) {
        // workaround for Issue 258 (https://code.google.com/p/google-plugin-for-eclipse/issues/detail?id=258)
        devDesignUrl = new URL(StringUtils.replace(devDesignUrl.toString(), " ", "%20"));
      }
      devClassLoader = new URLClassLoader(new URL[]{devDesignUrl, resolvedDevLibUrl}, null);
      devClassLoaders.put(gwtLocation, devClassLoader);
    }
    return devClassLoader;
  }

  public void activate() throws Exception {
    // do nothing
  }

  public byte[] getGeneratedResource(String resourceName) throws Exception {
    return null;
  }

  public ILogSupport getLogSupport() {
    return logSupport;
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // BrowserShell
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * Creates and returns the {@link IBrowserShell} instance for current platform using external
   * factory.
   */
  private IBrowserShell createBrowserShell() throws Exception {
    List<IBrowserShellFactory> factories =
        ExternalFactoriesHelper.getElementsInstances(
            IBrowserShellFactory.class,
            "com.google.gdt.eclipse.designer.hosted.2_2.browserShellFactory",
            "factory");
    for (IBrowserShellFactory factory : factories) {
      IBrowserShell shell = factory.create();
      if (shell != null) {
        return shell;
      }
    }
    // no shell has been created by factories
    if (isWindows64()) {
      // special message for windows
      throw new HostedModeException(HostedModeException.WIN32_NO_WINDOWS_64);
    }
    throw new HostedModeException(HostedModeException.UNSUPPORTED_OS);
  }

  /**
   * @return <code>true</code> while running Windows 64-bit.
   */
  private boolean isWindows64() {
    String osName = System.getProperty("os.name");
    String archName = System.getProperty("os.arch");
    if (!StringUtils.isEmpty(osName) && !StringUtils.isEmpty(archName)) {
      return osName.startsWith("Windows") && archName.indexOf("64") != -1;
    }
    return false;
  }

  /**
   * Forces an outstanding messages to be processed
   */
  public void runMessagesLoop() {
    try {
      while (Display.getCurrent().readAndDispatch()) {
        // wait
      }
    } catch (Throwable e) {
    }
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // IBrowserShellHost
  //
  ////////////////////////////////////////////////////////////////////////////
  public Object createModuleSpaceHost(String moduleName) throws Exception {
    ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
    Thread.currentThread().setContextClassLoader(getDevClassLoader());
    try {
      initializePersistentUnitCache();
      // create ShellModuleSpaceHost
      moduleSpaceHost =
          ReflectionUtils.invokeMethod(
              impl,
              "createModuleSpaceHost(java.lang.String,java.io.File,java.lang.String)",
              moduleName,
              null,
              getUserAgent());
      return moduleSpaceHost;
    } finally {
      Thread.currentThread().setContextClassLoader(oldContextClassLoader);
    }
  }

  private void initializePersistentUnitCache() throws Exception {
    // there are failures in tests, don't know why
    /*if (EnvironmentUtils.isTestingTime()) {
      return;
    }*/
    // do initialize
    /*try {
      File cacheDir = new File(SystemUtils.getJavaIoTmpDir(), SystemUtils.USER_NAME + "-gwtd");
      cacheDir.mkdirs();
      ClassLoader devClassLoader = getDevClassLoader();
      Class<?> builderClass =
          devClassLoader.loadClass("com.google.gwt.dev.javac.CompilationStateBuilder");
      ReflectionUtils.invokeMethod(
          builderClass,
          "init(com.google.gwt.core.ext.TreeLogger,java.io.File)",
          logSupport.getLogger(),
          cacheDir);
    } catch (Throwable e) {
    }*/
  }

  /**
   * @return the actual user agent, or "safari" if "warm up" mode.
   */
  private String getUserAgent() {
    if (browserShell == null) {
      return "safari";
    }
    return browserShell.getUserAgentString();
  }

  public Object createModuleSpace(String moduleName, Object msHost, ModuleSpace delegateModuleSpace)
      throws Exception {
    return ReflectionUtils.invokeMethod(
        impl,
        "createDelegatingModuleSpace(java.lang.Object,java.lang.String,java.lang.Object)",
        msHost,
        moduleName,
        delegateModuleSpace);
  }

  public DispatchIdOracle getDispatchIdOracle(Object delegate) throws Exception {
    if (dispatchIdOracle == null) {
      final Object dispatchIdOracleImpl =
          ReflectionUtils.invokeMethod2(delegate, "getDispatchIdOracle");
      dispatchIdOracle = new DispatchIdOracleImpl(dispatchIdOracleImpl);
    }
    return dispatchIdOracle;
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Utils
  //
  ////////////////////////////////////////////////////////////////////////////
  public static String getTemporaryDirectoryName(IJavaProject javaProject) {
    String logDir = javaProject.getProject().getLocation().toOSString() + File.separator + ".gwt";
    File logDirFile = new File(logDir);
    logDirFile.mkdirs();
    return logDir;
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // IHostedModeSupport, invocations of native code.
  //
  ////////////////////////////////////////////////////////////////////////////
  @SuppressWarnings("rawtypes")
  public boolean invokeNativeBoolean(String string, Class[] classes, Object[] objects) {
    try {
      return browserShell.getModuleSpace().invokeNativeBoolean(string, null, classes, objects);
    } catch (Throwable e) {
      throw ReflectionUtils.propagate(e);
    }
  }

  @SuppressWarnings("rawtypes")
  public String invokeNativeString(String string, Class[] classes, Object[] objects) {
    try {
      return (String) browserShell.getModuleSpace().invokeNativeObject(
          string,
          null,
          classes,
          objects);
    } catch (Throwable e) {
      throw ReflectionUtils.propagate(e);
    }
  }

  @SuppressWarnings("rawtypes")
  public void invokeNativeVoid(String string, Class[] classes, Object[] objects) {
    try {
      browserShell.getModuleSpace().invokeNativeVoid(string, null, classes, objects);
    } catch (Throwable e) {
      throw ReflectionUtils.propagate(e);
    }
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Inner classes
  //
  ////////////////////////////////////////////////////////////////////////////
  private static final class DispatchIdOracleImpl implements DispatchIdOracle {
    private final Object dispatchIdOracleImpl;

    private DispatchIdOracleImpl(Object dispatchIdOracleImpl) {
      this.dispatchIdOracleImpl = dispatchIdOracleImpl;
    }

    public int getDispId(String member) {
      try {
        return (Integer) ReflectionUtils.invokeMethod(
            dispatchIdOracleImpl,
            "getDispId(java.lang.String)",
            member);
      } catch (Throwable e) {
        throw ReflectionUtils.propagate(e);
      }
    }

    public DispatchClassInfo getClassInfoByDispId(int dispId) {
      try {
        final Object dispatchClassInfo =
            ReflectionUtils.invokeMethod(dispatchIdOracleImpl, "getClassInfoByDispId(int)", dispId);
        return new DispatchClassInfo() {
          public Member getMember(int dispId) {
            try {
              return (Member) ReflectionUtils.invokeMethod(
                  dispatchClassInfo,
                  "getMember(int)",
                  dispId);
            } catch (Throwable e) {
              throw ReflectionUtils.propagate(e);
            }
          }
        };
      } catch (Throwable e) {
        throw ReflectionUtils.propagate(e);
      }
    }
  }
}
TOP

Related Classes of com.google.gdt.eclipse.designer.hosted.tdt.HostedModeSupport$DispatchIdOracleImpl

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.