com.google.gdt.eclipse.designer.hosted.tdz.HostedModeSupport.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gdt.eclipse.designer.hosted.tdz.HostedModeSupport.java

Source

/*******************************************************************************
 * 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.tdz;

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

import org.apache.commons.lang.StringUtils;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.swt.widgets.Display;
import org.osgi.framework.Bundle;

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.classloader.GWTSharedClassLoader;
import com.google.gdt.eclipse.designer.hosted.tdz.log.LogSupport;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.cfg.BindingProperty;
import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.cfg.ModuleDefLoader;
import com.google.gwt.dev.cfg.Properties;
import com.google.gwt.dev.cfg.Property;
import com.google.gwt.dev.javac.CompilationState;
import com.google.gwt.dev.shell.ArtifactAcceptor;
import com.google.gwt.dev.shell.CompilingClassLoader;
import com.google.gwt.dev.shell.ModuleSpace;
import com.google.gwt.dev.shell.ModuleSpaceHost;
import com.google.gwt.dev.shell.ShellModuleSpaceHost;

import org.eclipse.wb.core.utils.external.ExternalFactoriesHelper;
import org.eclipse.wb.internal.core.utils.IOUtils2;
import org.eclipse.wb.internal.core.utils.reflect.ProjectClassLoader;

/**
 * Implementation for {@link IHostedModeSupport} for GWT 2.0. Also used as {@link IBrowserShellHost} while
 * creating {@link ModuleSpace} for current platform.
 * 
 * @author mitin_aa
 */
public final class HostedModeSupport implements IHostedModeSupport, IBrowserShellHost {
    private static GWTSharedClassLoader m_gwtSharedClassLoader;
    private final ClassLoader m_parentClassLoader;
    private final IModuleDescription m_moduleDescription;
    private final BrowserShell m_browserShell;
    private final IJavaProject m_javaProject;
    private ProjectClassLoader m_devClassLoader;
    private ShellModuleSpaceHost m_moduleSpaceHost;
    private File m_shellDirectory;
    private final LogSupport m_logSupport;
    private ModuleDef m_moduleDef;
    private TypeOracle m_typeOracle;

    ////////////////////////////////////////////////////////////////////////////
    //
    // Constructor
    //
    ////////////////////////////////////////////////////////////////////////////
    public HostedModeSupport(ClassLoader parentClassLoader, IModuleDescription moduleDescription) throws Exception {
        m_parentClassLoader = parentClassLoader;
        m_moduleDescription = moduleDescription;
        m_javaProject = moduleDescription.getJavaProject();
        // Logger
        m_logSupport = new LogSupport(TreeLogger.TRACE, m_javaProject);
        // Class loaders
        createClassLoaders();
        // Browser shell
        m_browserShell = (BrowserShell) createBrowserShell();
        m_browserShell.setHost(this);
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // ClassLoaders
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Creates the special {@link ClassLoader}'s to work with GWT "dev" classes and "user" classes.
     */
    private void createClassLoaders() throws Exception {
        m_devClassLoader = ProjectClassLoader.create(getSharedClassLoader(), m_javaProject);
        {
            Bundle bundle = Platform.getBundle("com.google.gdt.eclipse.designer.hosted.2_0.super");
            URL rootEntry = bundle.getEntry("");
            rootEntry = FileLocator.toFileURL(rootEntry);
            m_devClassLoader.addURL(rootEntry);
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // IHostedModeSupport
    //
    ////////////////////////////////////////////////////////////////////////////
    public void startup(String browserStartupUrl, String moduleName, IProgressMonitor monitor, int timeout)
            throws Exception {
        CompilingClassLoader.parentClassLoader = m_parentClassLoader;
        try {
            //long l = System.currentTimeMillis();
            m_browserShell.setUrl(browserStartupUrl, moduleName, timeout, new Runnable() {
                public void run() {
                    runMessagesLoop();
                }
            });
            //System.out.println("\tstartup time: " + (System.currentTimeMillis() - l));
        } finally {
            CompilingClassLoader.parentClassLoader = null;
        }
    }

    public void dispose() {
        m_browserShell.dispose();
        // dispose if initialized (may be not if Module loading failed)
        if (m_moduleSpaceHost != null) {
            m_moduleSpaceHost.getModuleSpace().dispose();
        }
        // dispose for project; if the same project used in another editor 
        // it would be added again by activating the project. 
        m_gwtSharedClassLoader.dispose(m_moduleDescription);
        m_logSupport.dispose();
        m_moduleSpaceHost = null;
        // clear static caches
        try {
            Class<?> clazz = m_gwtSharedClassLoader.loadClass("com.google.gwt.i18n.rebind.ClearStaticData");
            Method method = clazz.getDeclaredMethod("clear");
            method.setAccessible(true);
            method.invoke(null);
        } catch (Throwable e) {
        }
        try {
            Class<?> clazz = m_gwtSharedClassLoader
                    .loadClass("com.google.gwt.uibinder.rebind.model.OwnerFieldClass");
            Field mapField = clazz.getDeclaredField("FIELD_CLASSES");
            mapField.setAccessible(true);
            Map<?, ?> map = (Map<?, ?>) mapField.get(null);
            map.clear();
        } catch (Throwable e) {
        }
    }

    public IBrowserShell getBrowserShell() {
        return m_browserShell;
    }

    public ClassLoader getClassLoader() {
        return m_moduleSpaceHost.getClassLoader();
    }

    public ClassLoader getDevClassLoader() {
        return m_devClassLoader;
    }

    public Object findJType(String name) {
        return m_typeOracle.findType(name);
    }

    private ClassLoader getSharedClassLoader() throws Exception {
        // shared class loader for gwt-user.jar
        if (m_gwtSharedClassLoader == null) {
            m_gwtSharedClassLoader = new GWTSharedClassLoader(ModuleDef.class.getClassLoader(), new URL[] {});
        }
        activate();
        return m_gwtSharedClassLoader;
    }

    public void invalidateRebind(String typeName) {
    }

    public void activate() throws Exception {
        m_gwtSharedClassLoader.setActiveProject(m_moduleDescription);
        ModuleSpace.setLogger(getLogger());
    }

    public byte[] getGeneratedResource(String resourceName) throws Exception {
        File resourceFile = new File(m_shellDirectory, resourceName);
        if (!resourceFile.exists()) {
            return null;
        }
        return IOUtils2.readBytes(resourceFile);
    }

    public ILogSupport getLogSupport() {
        return m_logSupport;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // BrowserShell 
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Creates and returns the {@link IBrowserShell} instance for current platform using external factory.
     */
    @SuppressWarnings("unchecked")
    private IBrowserShell createBrowserShell() throws Exception {
        List<IBrowserShellFactory> factories = ExternalFactoriesHelper.getElementsInstances(
                IBrowserShellFactory.class, "com.google.gdt.eclipse.designer.hosted.2_0.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
    //
    ////////////////////////////////////////////////////////////////////////////
    //private final Set<String> alreadySeenModules = new HashSet<String>();
    /**
     * Load a module.
     * 
     * @param moduleName
     *            a Name of the module to load.
     * @param outDir
     * @return the loaded module.
     */
    private ModuleDef loadModule(String moduleName) throws Exception {
        //boolean assumeFresh = !alreadySeenModules.contains(moduleName);
        ModuleDef moduleDef = ModuleDefLoader.loadFromClassPath(getLogger(), moduleName, true/*!assumeFresh*/);
        //alreadySeenModules.add(moduleName);
        assert moduleDef != null : "Required module state is absent";
        return moduleDef;
    }

    public ModuleSpaceHost createModuleSpaceHost(final String moduleName) throws Exception {
        ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(m_devClassLoader);
        try {
            // Try to find an existing loaded version of the module def.
            //
            String outDir = getTemporaryDirectoryName(m_javaProject);
            m_moduleDef = loadModule(moduleName);
            assert m_moduleDef != null;
            fixUserAgentProperty(m_moduleDef);
            // TODO uncomment, if want to see/debug generators, like UiBinder
            //File genDir = new File(outDir);
            File genDir = null;
            // Create a sandbox for the module.
            m_shellDirectory = new File(outDir, ".tmp" + File.separator + "shell" + File.separator + moduleName);
            CompilationState compilationState = m_moduleDef.getCompilationState(getLogger());
            m_typeOracle = compilationState.getTypeOracle();
            m_moduleSpaceHost = new ShellModuleSpaceHost(getLogger(), compilationState, m_moduleDef, genDir,
                    m_shellDirectory, new ArtifactAcceptor() {
                        public void accept(TreeLogger logger, ArtifactSet newlyGeneratedArtifacts)
                                throws UnableToCompleteException {
                            // TODO: does we need this?
                        }
                    });
            return m_moduleSpaceHost;
        } finally {
            Thread.currentThread().setContextClassLoader(oldContextClassLoader);
        }
    }

    /**
     * Forcibly set 'user.agent' property to current platform.
     * http://fogbugz.instantiations.com/fogbugz/default.php?41513
     */
    private void fixUserAgentProperty(ModuleDef module) {
        Properties properties = module.getProperties();
        for (Property property : properties) {
            if ("user.agent".equals(property.getName())) {
                BindingProperty bindingProperty = (BindingProperty) property;
                bindingProperty.setAllowedValues(bindingProperty.getRootCondition(),
                        m_browserShell.getUserAgentString());
                return;
            }
        }
    }

    public TreeLogger getLogger() {
        return (TreeLogger) m_logSupport.getLogger();
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // 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 m_moduleSpaceHost.getModuleSpace().invokeNativeBoolean(string, null, classes, objects);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

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

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