Package org.jdesktop.wonderland.modules.sas.server

Source Code of org.jdesktop.wonderland.modules.sas.server.SasServer$SasLaunchInfo

/**
* Project Wonderland
*
* Copyright (c) 2004-2009, 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.modules.sas.server;

import java.io.Serializable;
import java.util.logging.Logger;
import org.jdesktop.wonderland.common.ExperimentalAPI;
import org.jdesktop.wonderland.common.cell.CellID;
import org.jdesktop.wonderland.modules.appbase.server.cell.AppConventionalCellMO;
import org.jdesktop.wonderland.modules.appbase.server.cell.AppConventionalCellMO.AppServerLauncher;
import org.jdesktop.wonderland.server.comms.WonderlandClientID;
import org.jdesktop.wonderland.server.comms.WonderlandClientSender;
import com.sun.sgs.app.ManagedObject;
import com.sun.sgs.app.ManagedReference;
import com.sun.sgs.app.AppContext;
import java.util.HashMap;
import java.util.LinkedList;
import org.jdesktop.wonderland.server.cell.CellMO;
import org.jdesktop.wonderland.server.cell.CellManagerMO;
import java.util.logging.Level;

/**
* Provides the main server-side logic for SAS. This singleton contains the Registry,
* the Distributor and all of the server-side communications components for SAS.
*
* @author deronj
*/

@ExperimentalAPI
public class SasServer implements ManagedObject, Serializable, AppServerLauncher {

    private static final Logger logger = Logger.getLogger(SasServer.class.getName());

    /** Helps clean up app info when the app is stopped by the cell. */
    private static class SasLaunchInfo implements Serializable{
        private CellID cellID;
        private String executionCapability;
        private ManagedReference providerRef;
        private SasLaunchInfo (CellID cellID, String executionCapability, ManagedReference providerRef) {
            this.cellID = cellID;
            this.executionCapability = executionCapability;
            this.providerRef = providerRef;
        }
    }

    static class LaunchRequest implements Serializable {
        CellID cellID;
        String executionCapability;
        String appName;
        String command;
        ManagedReference providerRef;

        LaunchRequest (CellID cellID, String executionCapability, String appName, String command) {
            this.cellID = cellID;
            this.executionCapability = executionCapability;
            this.appName = appName;
            this.command = command;
        }

        void setProvider (ManagedReference providerRef) {
            this.providerRef = providerRef;
        }

        @Override
        public String toString () {
            return "cell = " + cellID + ", executionCapability = " + executionCapability +
                "appName = " + appName + ", command = " + command;
        }
    }

    /**
     * A map of the app launch requests in flight for various cells.
     * "In flight" means that the request has been sent to a provider.
     * Note: We manage things so that only one launch request can be in flight at a time
     * for a particular app cell.
     */
    private HashMap<CellID,LaunchRequest> launchesInFlight = new HashMap<CellID,LaunchRequest>();

    /**
     * A map of the SAS providers which have connected, indexed by their execution capabilities
     * Note: because a provider may support multiple capabilities it may appear on more than one list.
     */
    private HashMap<String,LinkedList<ManagedReference>> execCapToProviderList =
        new HashMap<String,LinkedList<ManagedReference>>();

    /**
     * A list of the app launch requests which still must be honored.
     */
    private LaunchList pendingLaunches = new LaunchList();

    /**
     * A list of the app launch requests that have succeeded. This is the list
     * of currently running apps.
     */
     private LaunchList runningLaunches = new LaunchList();

    /**
     * Called when a new provider client connects to the SAS server.
     */
    public void providerConnected(WonderlandClientSender sender, WonderlandClientID clientID) {
        logger.info("Sas provider connected, clientID = " + clientID);
       
        // TODO: for now everything uses xremwin
        String execCap = "xremwin";

        ProviderProxy provider = new ProviderProxy(clientID, sender);
        ManagedReference providerRef = AppContext.getDataManager().createReference(provider);
        provider.addExecutionCapability(execCap);

        // Add to execution capability list
        LinkedList<ManagedReference> providers = execCapToProviderList.get(execCap);
        if (providers == null) {
            providers = new LinkedList<ManagedReference>();
            execCapToProviderList.put(execCap, providers);
        }
        providers.add(providerRef);

        logger.info("Provider added to xremwin list, clientID = " + clientID);

        // See if there are any pending launches
        try {
            tryPendingLaunches(execCap);
        } catch (InstantiationException ie) {
            logger.warning("Exception during new provider connection execution of pending launches.");
            logger.warning("Exception = " + ie);
        }

        // Mark server modified
        AppContext.getDataManager().markForUpdate(this);
    }

    /**
     * Called when provider client disconnects from the SAS server.
     * A reference to the provider proxy for that client is returned. It is the callers
     * responsibility to properly clean up the provider proxy.
     */
    ManagedReference providerDisconnected(WonderlandClientSender sender, WonderlandClientID clientID) {
        logger.info("Sas provider disconnnected, clientID = " + clientID);
        ManagedReference providerRefToRemove = null;

        // TODO: for now everything uses xremwin
        String execCap = "xremwin";

        // Remove provider from execution capability list
        LinkedList<ManagedReference> providers = execCapToProviderList.get(execCap);
        if (providers != null) {
            for (ManagedReference providerRef : providers) {
                ProviderProxy provider = (ProviderProxy) providerRef.get();
                if (provider.getClientID().equals(clientID)) {
                    providerRefToRemove = providerRef;
                    break;
                }
            }
            if (providerRefToRemove != null) {
                providers.remove(providerRefToRemove);
                persistProviderApps(providerRefToRemove, execCap);
            }

            if (providers.size() <= 0) {
                execCapToProviderList.remove(execCap);
            }
        }

        // Mark server modified
        AppContext.getDataManager().markForUpdate(this);
        return providerRefToRemove;

    }

    /**
     * {@inheritDoc}
     */
    public Object appLaunch (AppConventionalCellMO cell, String executionCapability, String appName,
                             String command)
        throws InstantiationException
    {
        logger.info("***** appLaunch, command = " + command);

        CellID cellID = cell.getCellID();

        // Construct the launch request
        // TODO: someday: allow multiple apps to be launched per cell.
        LaunchRequest launchReq = new LaunchRequest(cellID, executionCapability, appName, command);

        ManagedReference providerRef = requestLaunch(launchReq);
        return new SasLaunchInfo(launchReq.cellID, launchReq.executionCapability, providerRef);
    }

    private ManagedReference requestLaunch (LaunchRequest launchReq)
        throws InstantiationException
    {
        // Note: it is guaranteed that, during a warm start, the old SAS has already been removed
        // from this map.
        LinkedList<ManagedReference> providers = execCapToProviderList.get(launchReq.executionCapability);
        if (providers == null || providers.size() <= 0) {
            // No provider. Launch must pend
            logger.info("No SAS provider for " + launchReq.executionCapability + " is available.");
            logger.info("Launch attempt will pend.");
            pendingLaunches.add(launchReq);
            AppContext.getDataManager().markForUpdate(this);
            return null;
        }

        // TODO: someday: Right now we just try only the first provider. Eventually try multiple providers.
        ManagedReference providerRef = providers.getFirst();
        if (providerRef == null) {
            throw new InstantiationException("Cannot find a provider for " +
                                             launchReq.executionCapability);
        }

        // Now request the provider to launch the app
        launchReq.setProvider(providerRef);
        launchesInFlight.put(launchReq.cellID, launchReq);
        ProviderProxy provider = (ProviderProxy) providerRef.get();
        provider.tryLaunch(launchReq.cellID, launchReq.executionCapability, launchReq.appName,
                           launchReq.command);

        return providerRef;
    }
       
    /**
     * Called by the provider proxy to report the result of a launch
     */
    public void appLaunchResult (AppServerLauncher.LaunchStatus status, CellID cellID, String connInfo) {
        logger.info("############### SasServer: Launch result received");
        logger.info("status = " + status);
        logger.info("cellID = " + cellID);
        logger.info("connInfo = " + connInfo);

        // Get the request that we used to launch the app
        LaunchRequest launchReq = launchesInFlight.get(cellID);
        if (launchReq == null) {
            logger.warning("Cannot get app launch request for cell " + cellID);
            return;
        }
        launchesInFlight.remove(cellID);
        AppContext.getDataManager().markForUpdate(this);

        CellMO cell = CellManagerMO.getCell(cellID);
        if (cell == null) {
            logger.warning("Cannot find cell to which to report app launch result, launch request = " +
                           launchReq);
            return;
        }
        if (!(cell instanceof AppConventionalCellMO)) {
            logger.warning("Cell reported in app launch result is not an AppConventionalMO, launch request = " +
                           launchReq);
            return;
        }

        // TODO: someday: probably shouldn't do this if the provider tried to launch and it failed.
        // Probably should only do this if the provider wouldn't or couldn't launch for some reason.
        if (status != AppServerLauncher.LaunchStatus.SUCCESS || connInfo == null) {
            // The provider we tried cannot launch. Launch must pend.
            logger.warning("SAS provider launch failed with status " + status +
                           " and connection info " + connInfo +
                           " for launch request = " + launchReq);
            logger.warning("Launch attempt will pend until a provider is found.");
            pendingLaunches.add(launchReq);
            // Note: server has already been marked for update above

            // TODO: someday: at some point we need to give up and call cell.appLaunchResult with a failure
            // status. Need to implement a timeout.
            return;
        }

        // The app is now running
        runningLaunches.add(launchReq);

        ((AppConventionalCellMO)cell).appLaunchResult(status, connInfo);
    }
   
    /**
     * {@inheritDoc}
     */
    public void appStop (Object launchInfo) {
        SasLaunchInfo sasLaunchInfo = (SasLaunchInfo) launchInfo;

        // First, remove cell from the launches in flight map.
        launchesInFlight.remove(sasLaunchInfo.cellID);

        // Next, remove cell from the list of running apps
        runningLaunches.remove(sasLaunchInfo.cellID, sasLaunchInfo.executionCapability);

        // Next, remove cell from pending launch list.
        // TODO: someday: For now, this code assumes only one app launch per cell.
        pendingLaunches.remove(sasLaunchInfo.cellID, sasLaunchInfo.executionCapability);

        AppContext.getDataManager().markForUpdate(this);

        // Finally tell the provider to stop the app.
        //
        // If the provider was determined when appLaunch() was called, tell it to stop the app. */
        // But if the app had to pend waiting for a provider, we must notify all providers to
        // see which one launched the app.
        if (sasLaunchInfo.providerRef != null) {
            ProviderProxy provider = (ProviderProxy) sasLaunchInfo.providerRef.get();
            if (provider != null) {
                provider.appStop(sasLaunchInfo.cellID);
            }
        } else {
            LinkedList<ManagedReference> providers =
                execCapToProviderList.get(sasLaunchInfo.executionCapability);
            if (providers != null) {
                for (ManagedReference providerRef : providers) {
                    ProviderProxy provider = (ProviderProxy) providerRef.get();
                    if (provider != null) {
                        provider.appStop(sasLaunchInfo.cellID);
                    }
                }
            }
        }
    }

    /* TODO:someday:currently assumes a single provider per execution capability */
    private void tryPendingLaunches (String executionCapability) throws InstantiationException {
        LinkedList<LaunchRequest> reqs = pendingLaunches.getLaunches(executionCapability);
        if (reqs == null) {
            return;
        }
        LinkedList<LaunchRequest> reqsForTraversal = (LinkedList<LaunchRequest>) reqs.clone();
       
        for (LaunchRequest req : reqsForTraversal) {

            // TODO: Some of this code is dup from above in tryLaunch; share it

            // See if there are any more providers to try
            LinkedList<ManagedReference> providers = execCapToProviderList.get(executionCapability);
            if (providers == null || providers.size() <= 0) {
                continue;
            }
            // TODO: someday: weed out providers already tried
            ManagedReference providerRef = providers.getFirst();
            ProviderProxy provider = (ProviderProxy) providerRef.get();

            // Remove request from pending list while it is in flight */
            reqs.remove(req);

            // Now request the newly selected provider to launch the app
            launchesInFlight.put(req.cellID, req);
            provider.tryLaunch(req.cellID, req.executionCapability, req.appName, req.command);
        }
    }

    /**
     * Make the currently running apps persist by transferring them to the pending
     * launches list. They will be rerun the next time a suitable provider connects.
     * @param providerRef The provider whose running apps should be persisted.
     * @param execCap The execution capability of the running apps that should be persisted.
     * TODO:someday:currently assumes a single provider per execution capability
     */
    private void persistProviderApps (ManagedReference providerRef, String execCap) {
        LinkedList<LaunchRequest> reqsToRelaunch = new LinkedList<LaunchRequest>();

        logger.info("Persist provider apps");

        // First, persist the in-flight launches (i.e. the launches that have been requested
        // but have not yet occurred.
        LinkedList<CellID> cellsToRemove = new LinkedList<CellID>();
        for (CellID cellID : launchesInFlight.keySet()) {
            LaunchRequest launchReq = launchesInFlight.get(cellID);
            if (launchReq != null) {
                ProviderProxy reqProvider = (ProviderProxy) launchReq.providerRef.get();
                if (reqProvider.provides(execCap)) {
                    reqsToRelaunch.add(launchReq);
                    cellsToRemove.add(cellID);
                }
            }
        }
        for (CellID cellID : cellsToRemove) {
            launchesInFlight.remove(cellID);
        }
        cellsToRemove.clear();

        // Next, persist the running apps.
        LinkedList<LaunchRequest> launches = runningLaunches.getLaunches(execCap);
        if (launches != null) {
            for (LaunchRequest launchReq : launches) {
                reqsToRelaunch.add(launchReq);
                cellsToRemove.add(launchReq.cellID);
            }
        }
        for (CellID cellID : cellsToRemove) {
            runningLaunches.remove(cellID, execCap);
        }
        cellsToRemove.clear();

        // Note: previously we were just adding the launch request directly to the pendingLaunches
        // list. However, that wasn't handling the case where the new SAS provider connects before
        // the old SAS provider fully disconnects (and this method was fully done and its data committed).
        // Since the only way anything is launched off of the pending launch list is for a new SAS
        // provider to connect, we were, in the previous case, adding requests to the pending launch
        // list but nobody was ever actually reading this list to relaunch the apps.
        //
        // Instead, we call requestLaunch. If the new SAS provider has already connected it will
        // launch the app immediately. Otherwise, it will pend the launch and when the new SAS
        // provider later connects the request will be launched.

        for (LaunchRequest launchReq : reqsToRelaunch) {
            try {
                requestLaunch(launchReq);
            } catch (InstantiationException ex) {
                logger.log(Level.SEVERE, "App Instantiation error while trying to persist it", ex);
                continue;
            }
        }

        AppContext.getDataManager().markForUpdate(this);
    }
}
TOP

Related Classes of org.jdesktop.wonderland.modules.sas.server.SasServer$SasLaunchInfo

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.