Java tutorial
/******************************************************************************* * 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); } } } }