org.cruxframework.crux.plugin.gadget.linker.GadgetLinker.java Source code

Java tutorial

Introduction

Here is the source code for org.cruxframework.crux.plugin.gadget.linker.GadgetLinker.java

Source

/*
 * Copyright 2010 cruxframework.org.
 * 
 * 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.cruxframework.crux.plugin.gadget.linker;

import java.util.Iterator;

import org.cruxframework.crux.plugin.gadget.util.HangoutUtils;

import com.google.gwt.core.ext.LinkerContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.Artifact;
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.linker.CompilationResult;
import com.google.gwt.core.ext.linker.EmittedArtifact;
import com.google.gwt.core.ext.linker.LinkerOrder;
import com.google.gwt.core.ext.linker.LinkerOrder.Order;
import com.google.gwt.core.ext.linker.Shardable;
import com.google.gwt.core.linker.CrossSiteIframeLinker;
import com.google.gwt.dev.About;
import com.google.gwt.dev.util.DefaultTextOutput;
import com.google.gwt.dev.util.TextOutput;

/**
 * A Gadget does not use the {@code .nocache.js} file for the bootstrap. All bootstrap code is inserted
 * inside the gadget manifest file (the {@code .gadget.xml} file).
 * <p>
 * The linker also needs to change some script templates to integrate the application with the gadget container.
 * All requests for resources must be piped through the gadget proxy (using {@code gadgets.io.getProxyUrl()} 
 * method). 
 * @author Thiago da Rosa de Bustamante
 */
@LinkerOrder(Order.PRIMARY)
@Shardable
public final class GadgetLinker extends CrossSiteIframeLinker {
    private static final String SCRIPT_SRC_HANGOUTSAPI_SANDBOX = "<script src=\"//hangoutsapi.talkgadget.google.com/hangouts/_/api/hangout.js?v=1.3\" type=\"text/javascript\"></script>";
    private static final String SCRIPT_SRC_HANGOUTSAPI = "<script src=\"//talkgadget.google.com/hangouts/_/api/hangout.js?v=1.3\" type=\"text/javascript\"></script>";
    private static final String GADGET_LINKER_TEMPLATE_JS = "org/cruxframework/crux/plugin/gadget/linker/GadgetTemplate.js";
    private static final String GADGET_COMPUTE_SCRIPT_BASE_JS = "org/cruxframework/crux/plugin/gadget/linker/computeScriptBase.js";
    private static final String GADGET_INSTALL_SCRIPT_JS = "org/cruxframework/crux/plugin/gadget/linker/installScriptEarlyDownload.js";
    private static final String GADGET_PROCESS_METAS_JS = "org/cruxframework/crux/plugin/gadget/linker/processMetas.js";
    private static final String GADGET_WAIT_FOR_BODY_LOADED_JS = "org/cruxframework/crux/plugin/gadget/linker/waitForBodyLoaded.js";
    private static final String GADGET_SET_LOCALE_JS = "org/cruxframework/crux/plugin/gadget/linker/setGadgetLocale.js";
    private static final String GADGET_PROPERTIES_JS = "org/cruxframework/crux/plugin/gadget/linker/properties.js";
    private static final String GADGET_COMPUT_URL_FOR_RESOURCE_JS = "org/cruxframework/crux/plugin/gadget/linker/computeUrlForGadgetResource.js";
    private static final String HANGOUT_GADGET_COMPUT_URL_FOR_RESOURCE_JS = "org/cruxframework/crux/plugin/gadget/linker/computeUrlForHangoutGadgetResource.js";

    private ArtifactSet toLink;

    /**
     * @see com.google.gwt.core.linker.CrossSiteIframeLinker#getDescription()
     */
    @Override
    public String getDescription() {
        return "Crux Gadget";
    }

    @Override
    protected String getModulePrefix(TreeLogger logger, LinkerContext context, String strongName)
            throws UnableToCompleteException {
        TextOutput out = new DefaultTextOutput(context.isOutputCompact());

        // $wnd is the main window that the GWT code will affect and also the
        // location where the bootstrap function was defined. In iframe-based linkers,
        // $wnd is set to window.parent. Usually, in others, $wnd = window.
        // By default, $wnd is not set when the module starts, but a replacement for
        // installLocationIframe.js may set it.

        //BEGIN GADGET GWT 2.6.1 CHANGE
        out.print("var $wnd;");
        out.newlineOpt();
        out.print("if($wnd && $wnd." + context.getModuleFunctionName() + ") { $wnd = $wnd; } " + "else if(window."
                + context.getModuleFunctionName() + "){ $wnd = window; } " + "else { $wnd = window.parent; }");
        //END GADGET GWT 2.6.1 CHANGE

        out.newlineOpt();
        out.print("var __gwtModuleFunction = $wnd." + context.getModuleFunctionName() + ";");
        out.newlineOpt();
        out.print("var $sendStats = __gwtModuleFunction.__sendStats;");
        out.newlineOpt();
        out.print("$sendStats('moduleStartup', 'moduleEvalStart');");
        out.newlineOpt();
        out.print("var $gwt_version = \"" + About.getGwtVersionNum() + "\";");
        out.newlineOpt();
        out.print("var $strongName = '" + strongName + "';");
        out.newlineOpt();
        out.print("var $doc = $wnd.document;");

        // The functions for runAsync are set up in the bootstrap script so they
        // can be overriden in the same way as other bootstrap code is, however
        // they will be called from, and expected to run in the scope of the GWT code
        // (usually an iframe) so, here we set up those pointers.
        out.print("function __gwtStartLoadingFragment(frag) {");
        out.newlineOpt();
        String fragDir = getFragmentSubdir(logger, context) + '/';
        out.print("var fragFile = '" + fragDir + "' + $strongName + '/' + frag + '" + FRAGMENT_EXTENSION + "';");
        out.newlineOpt();
        out.print("return __gwtModuleFunction.__startLoadingFragment(fragFile);");
        out.newlineOpt();
        out.print("}");
        out.newlineOpt();
        out.print("function __gwtInstallCode(code) {return __gwtModuleFunction.__installRunAsyncCode(code);}");
        out.newlineOpt();

        // Even though we call the $sendStats function in the code written in this
        // linker, some of the compilation code still needs the $stats and
        // $sessionId
        // variables to be available.
        out.print("var $stats = $wnd.__gwtStatsEvent ? function(a) {return $wnd.__gwtStatsEvent(a);} : null;");
        out.newlineOpt();
        out.print("var $sessionId = $wnd.__gwtStatsSessionId ? $wnd.__gwtStatsSessionId : null;");
        out.newlineOpt();

        return out.toString();
    }

    /**
     * We need to save the original artifactSet received here to be able to re-emit the selection 
     * script when {@link #relink(TreeLogger, LinkerContext, ArtifactSet)} method is called.
     * @see com.google.gwt.core.ext.linker.impl.SelectionScriptLinker#link(com.google.gwt.core.ext.TreeLogger, com.google.gwt.core.ext.LinkerContext, com.google.gwt.core.ext.linker.ArtifactSet, boolean)
     */
    @Override
    public ArtifactSet link(TreeLogger logger, LinkerContext context, ArtifactSet artifacts, boolean onePermutation)
            throws UnableToCompleteException {
        toLink = new ArtifactSet(artifacts);

        ArtifactSet result = super.link(logger, context, toLink, onePermutation);
        return result;
    }

    /**
     * We must re-emit the selection script here, once the script is placed on the gadget manifest file.
     * <p>
     * It is necessary because the HTML code for the page is also placed on this same file. If we do not
     * re-generate the manifest file, hot deployment would not work for {@code .crux.xml} files.
     * @see com.google.gwt.core.ext.Linker#relink(com.google.gwt.core.ext.TreeLogger, com.google.gwt.core.ext.LinkerContext, com.google.gwt.core.ext.linker.ArtifactSet)
     */
    @Override
    public ArtifactSet relink(TreeLogger logger, LinkerContext context, ArtifactSet newArtifacts)
            throws UnableToCompleteException {
        permutationsUtil.setupPermutationsMap(toLink);
        ArtifactSet toReturn = new ArtifactSet(toLink);

        Iterator<Artifact<?>> iterator = newArtifacts.iterator();
        while (iterator.hasNext()) {
            toReturn.add(iterator.next());
        }

        EmittedArtifact art = emitSelectionScript(logger, context, toLink);
        if (art != null) {
            toReturn.add(art);
        }
        maybeOutputPropertyMap(logger, context, toReturn);
        maybeAddHostedModeFile(logger, context, toReturn, null);
        return toReturn;
    }

    /**
     * This method changes the gwt default behavior to emit the selection script into the manifest file.
     * @see com.google.gwt.core.ext.linker.impl.SelectionScriptLinker#emitSelectionScript(com.google.gwt.core.ext.TreeLogger, com.google.gwt.core.ext.LinkerContext, com.google.gwt.core.ext.linker.ArtifactSet)
     */
    @Override
    protected EmittedArtifact emitSelectionScript(TreeLogger logger, LinkerContext context, ArtifactSet artifacts)
            throws UnableToCompleteException {

        logger = logger.branch(TreeLogger.DEBUG, "Building gadget manifest", null);

        String bootstrap = "<script>" + generateSelectionScript(logger, context, artifacts) + "</script>";
        if (HangoutUtils.isHangoutGadget()) {
            if (HangoutUtils.useHangoutSandbox()) {
                bootstrap = SCRIPT_SRC_HANGOUTSAPI_SANDBOX + bootstrap;
            } else {
                bootstrap = SCRIPT_SRC_HANGOUTSAPI + bootstrap;
            }

        }

        StringBuffer manifest = new StringBuffer();

        GadgetManifestGenerator gadgetManifestGenerator = new GadgetManifestGenerator(logger,
                context.getModuleName());
        manifest.append(gadgetManifestGenerator.generateGadgetManifestFile());
        String manifestName = gadgetManifestGenerator.getManifestName();
        replaceAll(manifest, "__BOOTSTRAP__", bootstrap);

        return emitString(logger, manifest.toString(), manifestName);
    }

    /**
     * Locale handling is different for gadgets, so we need to insert a specific script
     * for it. 
     * @see com.google.gwt.core.linker.CrossSiteIframeLinker#fillSelectionScriptTemplate(java.lang.StringBuffer, com.google.gwt.core.ext.TreeLogger, com.google.gwt.core.ext.LinkerContext, com.google.gwt.core.ext.linker.ArtifactSet, com.google.gwt.core.ext.linker.CompilationResult)
     */
    @Override
    protected String fillSelectionScriptTemplate(StringBuffer selectionScript, TreeLogger logger,
            LinkerContext context, ArtifactSet artifacts, CompilationResult result)
            throws UnableToCompleteException {
        super.fillSelectionScriptTemplate(selectionScript, logger, context, artifacts, result);

        /*   
         * Gadget iframe URLs are generated with the locale in the URL as a
         * lang/country parameter pair (e.g. lang=en&country=US) in lieu of the
         * single locale parameter.
         * ($wnd.__gwt_Locale is read by the property provider in I18N.gwt.xml)
         */
        includeJs(selectionScript, logger, getJsSetGadgetLocale(context), "__GADGET_SET_LOCALE__");
        /*
         * Hangout gadgets need to use absolute urls for any script or css inclusions. 
         */
        if (HangoutUtils.isHangoutGadget()) {
            String gadgetDeployURL = HangoutUtils.getDeployURL();
            replaceAll(selectionScript, "__DEPLOY_URL__", gadgetDeployURL);
        }
        return selectionScript.toString();
    }

    /**
     * 
     */
    @Override
    protected String getJsComputeUrlForResource(LinkerContext context) {
        if (HangoutUtils.isHangoutGadget()) {
            return HANGOUT_GADGET_COMPUT_URL_FOR_RESOURCE_JS;
        } else {
            return GADGET_COMPUT_URL_FOR_RESOURCE_JS;
        }
    }

    /**
     * Gets the setLocale template for gadgets
     */
    protected String getJsSetGadgetLocale(LinkerContext context) {
        return GADGET_SET_LOCALE_JS;
    }

    /**
     * Gets the setLocale template for properties
     */
    @Override
    protected String getJsProperties(LinkerContext context) {
        return GADGET_PROPERTIES_JS;
    }

    /**
     * @see com.google.gwt.core.linker.CrossSiteIframeLinker#getJsComputeScriptBase(com.google.gwt.core.ext.LinkerContext)
     */
    @Override
    protected String getJsComputeScriptBase(LinkerContext context) {
        return GADGET_COMPUTE_SCRIPT_BASE_JS;
    }

    /**
     * @see com.google.gwt.core.linker.CrossSiteIframeLinker#getJsInstallScript(com.google.gwt.core.ext.LinkerContext)
     */
    @Override
    protected String getJsInstallScript(LinkerContext context) {
        return GADGET_INSTALL_SCRIPT_JS;
    }

    /**
     * @see com.google.gwt.core.linker.CrossSiteIframeLinker#getJsProcessMetas(com.google.gwt.core.ext.LinkerContext)
     */
    @Override
    protected String getJsProcessMetas(LinkerContext context) {
        return GADGET_PROCESS_METAS_JS;
    }

    /**
     * @see com.google.gwt.core.linker.CrossSiteIframeLinker#getJsWaitForBodyLoaded(com.google.gwt.core.ext.LinkerContext)
     */
    @Override
    protected String getJsWaitForBodyLoaded(LinkerContext context) {
        return GADGET_WAIT_FOR_BODY_LOADED_JS;
    }

    /**
     * @see com.google.gwt.core.linker.CrossSiteIframeLinker#getSelectionScriptTemplate(com.google.gwt.core.ext.TreeLogger, com.google.gwt.core.ext.LinkerContext)
     */
    @Override
    protected String getSelectionScriptTemplate(TreeLogger logger, LinkerContext context) {
        return GADGET_LINKER_TEMPLATE_JS;
    }
}