Package org.jdesktop.wonderland.webserver

Source Code of org.jdesktop.wonderland.webserver.RunAppServer

/**
* Open Wonderland
*
* Copyright (c) 2010, Open Wonderland Foundation, All Rights Reserved
*
* Redistributions in source code form must reproduce the above
* copyright and this condition.
*
* The contents of this file are subject to the GNU General Public
* License, Version 2 (the "License"); you may not use this file
* except in compliance with the License. A copy of the License is
* available at http://www.opensource.org/licenses/gpl-license.php.
*
* The Open Wonderland Foundation designates this particular file as
* subject to the "Classpath" exception as provided by the Open Wonderland
* Foundation in the License file that accompanied this code.
*/

/**
* Project Wonderland
*
* Copyright (c) 2004-2010, Sun Microsystems, Inc., All Rights Reserved
*
* Redistributions in source code form must reproduce the above
* copyright and this condition.
*
* The contents of this file are subject to the GNU General Public
* License, Version 2 (the "License"); you may not use this file
* except in compliance with the License. A copy of the License is
* available at http://www.opensource.org/licenses/gpl-license.php.
*
* Sun designates this particular file as subject to the "Classpath"
* exception as provided by Sun in the License file that accompanied
* this code.
*/
package org.jdesktop.wonderland.webserver;

import com.sun.enterprise.util.net.NetUtils;
import org.jdesktop.wonderland.utils.AppServerMonitor;
import org.jdesktop.wonderland.utils.RunUtil;
import org.jdesktop.wonderland.webserver.launcher.WebServerLauncher;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipInputStream;
import org.glassfish.api.deployment.DeployCommandParameters;
import org.glassfish.api.embedded.ContainerBuilder;
import org.glassfish.api.embedded.EmbeddedFileSystem;
import org.glassfish.api.embedded.LifecycleException;
import org.glassfish.api.embedded.Server;
import org.jdesktop.wonderland.client.jme.WonderlandURLStreamHandlerFactory;
import org.jdesktop.wonderland.common.NetworkAddress;
import org.jdesktop.wonderland.common.modules.ModuleInfo;
import org.jdesktop.wonderland.modules.Module;
import org.jdesktop.wonderland.modules.ModuleAttributes;
import org.jdesktop.wonderland.modules.service.ModuleManager;
import org.jdesktop.wonderland.modules.service.ModuleManager.TaggedModule;
import org.jdesktop.wonderland.utils.Constants;
import org.jdesktop.wonderland.utils.FileListUtil;
import org.jdesktop.wonderland.utils.SystemPropertyUtil;

/**
*
* @author jkaplan
*/
public class RunAppServer {
    // logger
    private static final Logger logger =
            Logger.getLogger(RunAppServer.class.getName());
   
    // singleton instance
    private static WonderlandAppServer appServer;
   
    public RunAppServer() throws IOException {
        // set up URL handlers for Wonderland types
        URL.setURLStreamHandlerFactory(new WonderlandURLStreamHandlerFactory());

        // now load the properties
        setupProperties();

        // check if we need to make any changes
        if (Boolean.parseBoolean(
                System.getProperty(Constants.WEBSERVER_NEWVERSION_PROP)))
        {
            // replace old versions of modules with newer versions
            updateModules();
           
            // write the web server's document root
            writeDocumentRoot();

            // write DTDs for local deployments
            writeSchemas();

            // write the updated webapps
            writeWebApps();
        }

        // create and start the appserver -- the hostname will be set
        // as a sideeffect of the start call
        try {
            createAppServer();
            getAppServer().start();
        } catch (LifecycleException le) {
            throw new IOException(le);
        }

        // deploy built-in web apps
        deployWebApps();

        // notify the deployer that we are ready to start deploying other apps
        getAppServer().setDeployable(true);

        // redeploy any other modules, including web modules,
        // that haven't yet been deployed.  This will also
        // install all pending modules
        ModuleManager.getModuleManager().redeployAll();

        // start accepting secure connections to the web server (needed
        // by the services that start when we fire the startup complete
        // event below)
        DefaultSAM.setStarted(true);

        // now that all the modules are deployed, notify anyone waiting
        // for startup
        AppServerMonitor.getInstance().fireStartupComplete();
    }

    /**
     * Set up some important properties needed everywhere, like the hostname
     * and webserver URL
     * @param appServerHostname the hostname as detected by the application
     * server.  This will be used, unless we have specified another value.
     */
    protected void setupProperties() {

        String host = System.getProperty(Constants.WEBSERVER_HOST_PROP);
       
        logger.fine("[RunAppServer] host property: " + host);

        if (host != null) {
            // a host was specified -- resolve it
            host = resolveAddress(host);
        }

        logger.fine("[RunAppServer] resolved host: " + host);

        // if the host does not exist or does not resolve, try glassfish's
        // guess
        if (host == null) {
            try {
                host = NetUtils.getCanonicalHostName();
            } catch (UnknownHostException uhe) {
                // ignore
            }
        }

        logger.fine("[RunAppServer] glassfish host: " + host);
       
        // still no luck -- use our best guess
        if (host == null) {
            host = resolveAddress(null);
        }

        String internalHost = System.getProperty(Constants.WEBSERVER_HOST_INTERNAL_PROP);
        if (internalHost == null) {
            internalHost = host;
        }

        logger.fine("[RunAppServer] wonderland host: " + host);
       
        // set the system property
        System.setProperty(Constants.WEBSERVER_HOST_PROP, host);
        System.setProperty(Constants.WEBSERVER_HOST_INTERNAL_PROP, internalHost);

        // set the web server URL based on the hostname and port
        if (System.getProperty(Constants.WEBSERVER_URL_PROP) == null) {
            System.setProperty(Constants.WEBSERVER_URL_PROP,
                "http://" + System.getProperty(Constants.WEBSERVER_HOST_PROP).trim() +
                ":" + System.getProperty(Constants.WEBSERVER_PORT_PROP).trim() + "/");
        }

        if (System.getProperty(Constants.WEBSERVER_URL_INTERNAL_PROP) == null) {
            System.setProperty(Constants.WEBSERVER_URL_INTERNAL_PROP,
                "http://" + System.getProperty(Constants.WEBSERVER_HOST_INTERNAL_PROP).trim() +
                ":" + System.getProperty(Constants.WEBSERVER_PORT_PROP).trim() + "/");
        }

        // make sure we load all libraries in the embedded Glassfish instance
        System.setProperty("org.glassfish.embed.Server.IncludeAllLibs", "true");

        // output the derby log file to a sensible location
        System.setProperty("derby.stream.error.file",
                           new File(RunUtil.getRunDir(), "derby.log").getPath());

        // set the run directory to the subsituted value, so that it can
        // be used in domain.xml
        System.setProperty(Constants.RUN_DIR_PROP,
                           SystemPropertyUtil.getProperty(Constants.RUN_DIR_PROP));
    }

    /**
     * Resolve the host address property into a hostname.  The mechanics of
     * this are mostly encapsulated in NetworkAddress
     * @param prop the property value to base our lookup on (may be null)
     * @return the public address we found
     */
    private static String resolveAddress(String prop) {
        String hostAddress = null;

        try {
            hostAddress = NetworkAddress.getPrivateLocalAddress(prop).getHostAddress();

            if (prop == null || prop.length() == 0) {
                logger.info("Local address " + hostAddress +
                            " was chosen from the list of interfaces");
            } else {
                logger.info("Local address " + hostAddress +
                            " was determined by using " + prop);
            }

            return hostAddress;
        } catch (UnknownHostException e) {
            logger.log(Level.WARNING, "Unable to get Local address using " +
                       prop, e);

            try {
                hostAddress = NetworkAddress.getPrivateLocalAddress().getHostAddress();
                logger.info("chose private local address " + hostAddress +
                            " from the list of interfaces: " + e.getMessage());
            } catch (UnknownHostException ee) {
                logger.log(Level.WARNING, "Unable to determine private " +
                           "local address, using localhost", ee);

                hostAddress = "localhost";
            }
        }

        return hostAddress;
    }


    protected void deployWebApps() throws IOException {
        WonderlandAppServer as = getAppServer();

        // deploy all webapps
        File deployDir = new File(RunUtil.getRunDir(), getWebappDir());
        for (File war : deployDir.listFiles(WAR_FILTER)) {
            logger.info("Deploying " + war.getPath());
           
            try {
                as.deploy(war);
            } catch (Exception excp) {
                // ignore any exception and continue
                logger.log(Level.WARNING, "Error deploying " + war, excp);
            }
        }
    }

    protected void writeDocumentRoot() throws IOException {
        File docDir = new File(RunUtil.getRunDir(), "docRoot");
        docDir.mkdirs();

        // figure out the set of files to add or remove
        List<String> addFiles = new ArrayList<String>();
        List<String> removeFiles = new ArrayList<String>();
        FileListUtil.compareDirs("META-INF/docroot", docDir,
                                 addFiles, removeFiles);

        for (String removeFile : removeFiles) {
            File file = new File(docDir, removeFile);
            file.delete();
        }

        for (String addFile : addFiles) {
            String fullPath = "/docroot/" + addFile;
            InputStream fileIs =
                    WebServerLauncher.class.getResourceAsStream(fullPath);

            RunUtil.writeToFile(fileIs, new File(docDir, addFile));
            fileIs.close();
        }

        // write the updated checksum list
        RunUtil.extract(getClass(), "/META-INF/docroot/files.list", docDir);
    }

    protected void writeWebApps() throws IOException {
        // write to a subdirectory of the default temp directory
        File deployDir = new File(RunUtil.getRunDir(), getWebappDir());
        deployDir.mkdirs();

        // figure out the set of files to add or remove
        List<String> addFiles = new ArrayList<String>();
        List<String> removeFiles = new ArrayList<String>();
        FileListUtil.compareDirs("META-INF/" + getWebappDir(), deployDir,
                                 addFiles, removeFiles);

        // remove the files to remove
        for (String removeFile : removeFiles) {
            File remove = new File(deployDir, removeFile);

            // files have been extracted into directories
            if (remove.isDirectory()) {
                RunUtil.deleteDir(remove);
            }
        }

        for (String addFile : addFiles) {
            String fullPath = "/" + getWebappDir() + "/" + addFile;

            // make sure to clear the directory before we write to it
            File existingDir = new File(deployDir, addFile);
            if (existingDir.exists() && existingDir.isDirectory()) {
                RunUtil.deleteDir(existingDir);
            }

            RunUtil.extractJar(getClass(), fullPath, deployDir);
        }

        // write the updated checksum list
        RunUtil.extract(getClass(), "/META-INF/" + getWebappDir() +
                                    "/files.list", deployDir);
    }

    protected void writeSchemas() throws IOException {
        // issue #1191: for offline instances of Wonderland, make sure
        // we have a copy of all the dtd files locally, otherwise
        // glassfish will fail
        File install_dir = new File(RunUtil.getRunDir(), "web_install");
        File lib_dir = new File(install_dir, "lib");
        File dtds_dir = new File(lib_dir, "dtds");
        File schemas_dir = new File(lib_dir, "schemas");

        // clear the directory & recreate it
        if (dtds_dir.exists()) {
            RunUtil.deleteDir(dtds_dir);
        }
        if (schemas_dir.exists()) {
            RunUtil.deleteDir(schemas_dir);
        }

        // extract the dtds
        ZipInputStream zis = new ZipInputStream(
                getClass().getResourceAsStream("/webserver/schemas/schemas.zip"));
        RunUtil.extractZip(zis, lib_dir);
    }

    /**
     * Get the directory where webapps are stored and deployed
     * @return the name of the webapp directory
     */
    protected String getWebappDir() {
        return "deploy";
    }

    /**
     * Update any system-installed module to be the latest version from
     * the Wonderland.jar file.
     * @throws IOException if there is an error reading or writing modules
     */
    protected void updateModules() throws IOException {
        ModuleManager mm = ModuleManager.getModuleManager();

        // create the directory to extract modules to, if it doesn't already
        // exist
        File moduleDir = RunUtil.createTempDir("module", ".jar");

        // read the list of modules and their checksums from the jar file
        Map<String, String> checksums =
                FileListUtil.readChecksums("META-INF/modules");

        // get the list of all installed module with the "system-installed"
        // key set.  This is set on all modules installed by the system
        Map<String, Module> installed =
                mm.getInstalledModulesByKey(ModuleAttributes.SYSTEM_MODULE);

        // get the checksum of any module that has a checksum.  As a
        // side-effect, any module with a checksum is removed from the
        // list of installed modules, so that list can be used to decide
        // which modules to uninstall
        Map<String, String[]> installedChecksums = getChecksums(installed);

        // add all modules remaining in the installed list to the
        // uninstall list.  These are modules that were installed by an
        // old version of Wonderland and do not have a filename or
        // checksum attribute set.
        Collection<String> uninstall = new ArrayList<String>(installed.keySet());

        // now go through the checksums of old and new modules to determine
        // which modules need to be installed and which are unchanged
        List<TaggedModule> install = new ArrayList<TaggedModule>();
        for (Map.Entry<String, String> checksum : checksums.entrySet()) {

            // compare an existing checksum to an old checksum. If the
            // old checksum doesn't exist or is different than the new
            // checksum, install the new file.  This will overwrite the old
            // checksum in the process.
            String[] installedChecksum = installedChecksums.remove(checksum.getKey());
            if (installedChecksum == null ||
                    !installedChecksum[0].equals(checksum.getValue()))
            {
                install.add(createTaggedModule(checksum.getKey(),
                                               checksum.getValue(),
                                               moduleDir));
            }
        }

        // any modules not removed from the installedChecksums list are
        // old modules that were installed by a previous version of Wonderland
        // (not by the user) and aren't in the new module list.  We need to
        // remove these modules
        for (String[] moduleDesc : installedChecksums.values()) {
            uninstall.add(moduleDesc[1]);
        }

        // uninstall any modules on the uninstall list
        logger.warning("Uninstall: " + uninstall);
        mm.addToUninstall(uninstall);
       
        // install any modules on the install list
        String installList = "";
        for (TaggedModule tm : install) {
            installList += " " + tm.getFile().getName();
        }
        logger.warning("Install: " + installList);
        mm.addTaggedToInstall(install);
    }

    private Map<String, String[]> getChecksums(Map<String, Module> modules) {
        Map<String, String[]> out = new HashMap<String, String[]>();

        for (Iterator<Module> i = modules.values().iterator(); i.hasNext();) {
            ModuleInfo info = i.next().getInfo();
            String filename = info.getAttribute(ModuleAttributes.FILENAME);
            String checksum = info.getAttribute(ModuleAttributes.CHECKSUM);

            if (filename != null) {
                // add both the checksum and the module name
                String[] desc = new String[2];
                desc[0] = checksum;
                desc[1] = info.getName();

                // add to the list of files with checksums
                out.put(filename, desc);

                // remove from the installed list
                i.remove();
            }
        }

        return out;
    }

    private TaggedModule createTaggedModule(String file, String checksum,
                                            File moduleDir)
        throws IOException
    {
        // extract the file
        String fullPath = "/modules/" + file;
        File extracted = RunUtil.extract(getClass(), fullPath, moduleDir);

        // now generate the attributes
        Map<String, String> attributes = new HashMap<String, String>();
        attributes.put(ModuleAttributes.SYSTEM_MODULE, String.valueOf(true));
        attributes.put(ModuleAttributes.FILENAME, file);
        attributes.put(ModuleAttributes.CHECKSUM, checksum);

        // create the tagged module
        return new TaggedModule(extracted, attributes);
    }

    // get the main instance
    synchronized static WonderlandAppServer getAppServer() {
        return appServer;
    }

    synchronized static void createAppServer()
        throws IOException
    {
        if (appServer != null) {
            throw new IllegalStateException("App server already created.");
        }

        File install_dir = new File(RunUtil.getRunDir(), "web_install");
        File instance_dir = new File(RunUtil.getRunDir(), "web_run");
        File module_dir = new File(install_dir, "modules");
        File docroot_dir = new File(RunUtil.getRunDir(), "docRoot");

        File logDir = new File(SystemPropertyUtil.getProperty("wonderland.log.dir"));
        File logFile = new File(logDir, "web_server.log");

        // set environment variables that will be substituted into
        // domain.xml
        System.setProperty("org.jdesktop.wonderland.docRoot",
                           docroot_dir.toString());

        // make directories
        install_dir.mkdirs();
        instance_dir.mkdirs();
        module_dir.mkdirs();

        // extract the domain.xml file
        File domainXml = RunUtil.extract(RunAppServer.class,
                                         "/domain.xml", install_dir);

        EmbeddedFileSystem.Builder efsBuilder = new EmbeddedFileSystem.Builder();
        efsBuilder.installRoot(install_dir);
        efsBuilder.instanceRoot(instance_dir);
        efsBuilder.configurationFile(domainXml);

        Server.Builder serverBuilder = new Server.Builder("test");
        serverBuilder.embeddedFileSystem(efsBuilder.build());
        //serverBuilder.setLogFile(logFile);

        Server server = serverBuilder.build();

        String portStr = System.getProperty(Constants.WEBSERVER_PORT_PROP).trim();
        int port = Integer.parseInt(portStr);
        server.createPort(port);
        server.addContainer(ContainerBuilder.Type.all);

        // setup and launch
        appServer = new WonderlandAppServer(server);
    }
   
    private static final FilenameFilter WAR_FILTER = new FilenameFilter() {
        public boolean accept(File dir, String name) {
            return name.endsWith(".war");
        }
    };
}
TOP

Related Classes of org.jdesktop.wonderland.webserver.RunAppServer

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.