Package org.jdesktop.wonderland.modules.appbase.client.cell

Source Code of org.jdesktop.wonderland.modules.appbase.client.cell.AppConventionalCell$AppConventionalMessageReceiver

/**
* Open Wonderland
*
* Copyright (c) 2011, 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-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.appbase.client.cell;

import java.text.MessageFormat;
import java.util.ResourceBundle;
import org.jdesktop.wonderland.common.cell.CellID;
import org.jdesktop.wonderland.common.cell.messages.CellMessage;
import org.jdesktop.wonderland.common.cell.state.CellClientState;
import org.jdesktop.wonderland.client.cell.CellCache;
import org.jdesktop.wonderland.client.cell.ChannelComponent;
import org.jdesktop.wonderland.client.comms.WonderlandSession;
import org.jdesktop.wonderland.client.hud.CompassLayout.Layout;
import org.jdesktop.wonderland.client.hud.HUD;
import org.jdesktop.wonderland.client.hud.HUDManagerFactory;
import org.jdesktop.wonderland.client.hud.HUDMessage;
import org.jdesktop.wonderland.common.ExperimentalAPI;
import org.jdesktop.wonderland.modules.appbase.common.cell.AppConventionalCellClientState;
import org.jdesktop.wonderland.modules.appbase.common.cell.AppConventionalCellSetConnectionInfoMessage;
import org.jdesktop.wonderland.common.cell.CellStatus;
import org.jdesktop.wonderland.modules.appbase.common.cell.AppConventionalCellAppExittedMessage;
import org.jdesktop.wonderland.modules.appbase.client.App2D;
import org.jdesktop.wonderland.modules.appbase.client.FirstVisibleInitializer;

/**
* The client-side cell for an 2D conventional application.
*
* @author deronj
*/
@ExperimentalAPI
public abstract class AppConventionalCell extends App2DCell {
    private static final ResourceBundle BUNDLE = ResourceBundle.getBundle(
            "org/jdesktop/wonderland/modules/appbase/client/Bundle");
   
    /** The session used by the cell cache of this cell to connect to the server */
    private WonderlandSession cellCacheSession;
    /** The user-visible app name. */
    protected String appName;
    /** Is this a SAS- or user-launched app? */
    protected String launchLocation;
    /** If a user-launched app, who is the launching user?  */
    protected String launchUser;
    /** The execution command. */
    protected String command;
    /** The connection info. */
    protected String connectionInfo;
    /** Indicates that this cell is a slave and has connectedToTheApp. */
    private boolean slaveStarted;
    /** Indicates that the cell renderer for this cell has been created. */
    private boolean cellRendererExists;

    /**
     * Creates a new instance of AppConventionalCell.
     *
     * @param cellID The ID of the cell.
     * @param cellCache the cell cache which instantiated, and owns, this cell.
     */
    public AppConventionalCell(CellID cellID, CellCache cellCache) {
        super(cellID, cellCache);
        cellCacheSession = cellCache.getSession();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setClientState(CellClientState clientState) {
        super.setClientState(clientState);

        AppConventionalCellClientState state = (AppConventionalCellClientState) clientState;
        appName = state.getAppName();
        launchLocation = state.getLaunchLocation();
        launchUser = state.getLaunchUser();
        command = state.getCommand();

        if (launchLocation.equalsIgnoreCase("user") &&
            launchUser.equals(cellCacheSession.getUserID().getUsername())) {

            // Master case: nothing to do

        } else {

            // Slave case
            connectionInfo = state.getConnectionInfo();
            logger.info("Initial connection info value for slave = " + connectionInfo);
        }
    }

    /** {@inheritDoc} */
    @Override
    protected void setStatus(CellStatus status, boolean increasing) {
        super.setStatus(status, increasing);

        if (status == CellStatus.INACTIVE && increasing) {
            AppConventionalMessageReceiver mr = new AppConventionalMessageReceiver();
            channel.addMessageReceiver(AppConventionalCellSetConnectionInfoMessage.class, mr);
            channel.addMessageReceiver(AppConventionalCellAppExittedMessage.class, mr);
        } else if (status == CellStatus.INACTIVE && !increasing) {
            channel.removeMessageReceiver(AppConventionalCellSetConnectionInfoMessage.class);
            channel.removeMessageReceiver(AppConventionalCellAppExittedMessage.class);
        }

        // Keep track of whether the cell renderer has been created
        if (status == CellStatus.ACTIVE) {
            if (increasing) {
                cellRendererExists = true;
            } else {
                cellRendererExists = false;
            }

            startSlaveIfReady();
        }

        // Launch the app when it is visible for the first time
        if (status == CellStatus.VISIBLE && increasing) {

            // Slave case
            //
            // Slaves must wait to connect until valid connection info is known. This can happen
            // in one of two ways. If the slave cell was loaded into this client AFTER the master
            // app started the connection info will already be known (i.e. non-null). Otherwise,
            // if the slave cell was loaded into this client BEFORE the master app started this
            // client will eventually receive a SetConnectionInfo message whic contains the
            // connection info.

            startSlaveIfReady();
        }

        // TODO: it would be a good idea to disconnect the slave at some point
        // (INACTIVE && !increasing?) to save on resources.
    }

    /**
     * Handle a setConnectionInfo message
     * @param message the message
     */
    void handleConnectionInfo(AppConventionalCellSetConnectionInfoMessage message) {
        setConnectionInfo(message.getConnectionInfo());
    }

    /**
     * Handle an app exitted message
     * @param message the exited message
     */
    void handleAppExitted(final AppConventionalCellAppExittedMessage message) {
        String text = BUNDLE.getString("App_Exit");
        text = MessageFormat.format(text, message.getAppName(), message.getExitValue());
       
        HUD mainHUD = HUDManagerFactory.getHUDManager().getHUD("main");
        HUDMessage hudMessage = mainHUD.createMessage(text);
        hudMessage.setPreferredLocation(Layout.NORTHEAST);
        mainHUD.addComponent(hudMessage);
        hudMessage.setVisible(true);
        hudMessage.setVisible(false, 10000);
    }

    /**
     * This is called when the server sends the connection info.
     */
    synchronized void setConnectionInfo (String connInfo) {
       
        // Has the connection info changed? If not, just return
        if (connectionInfo == null) {
            if (connInfo == null) {
                return;
            }
        } else {
            if (connectionInfo.equals(connInfo)) {
                return;
            }
        }
       
        connectionInfo = connInfo;

        // The connection info has changed. Start the app.
        startSlaveIfReady();
    }

    /**
     * Starts the slave if all preconditions are satisified. Before we can start the slave
     * we must have a cell renderer and we must have a connection info.
     */
    private void startSlaveIfReady() {
        if (slaveStarted) return;

        if (cellRendererExists && connectionInfo != null) {
            startTheSlave(connectionInfo);
        }
    }

    private void startTheSlave (String connectionInfo) {
        logger.info("Starting app slave.");
        app = startSlave(connectionInfo, fvi);
        if (app != null) {
            slaveStarted = true;
            logger.info("Connected slave to app at " + connectionInfo);
        } else {
            slaveStarted = false;
            logger.warning("Could not create slave app, connectionInfo = " + connectionInfo);
        }
    }

    /** Information returned from startMaster. */
    public static class StartMasterReturnInfo {
        /** The app created. */
        public App2D app;
        /** Subclass-specific data for making a peer-to-peer connection between master and slave. */
        public String connInfo;
        public StartMasterReturnInfo (App2D app, String connInfo) {
            this.app = app;
            this.connInfo = connInfo;
        }                                                                       
    }


    /**
     * Launch a master client.
     * @param appName The name of the app.
     * @param command The command string which launches the master app program (used only by master).
     * @return The app created and the subclass-specific connect info.
     */
    protected abstract StartMasterReturnInfo startMaster(String appName, String command,
                                                         FirstVisibleInitializer fvi);
 
    /**
     * Launch a slave client.
     * @param connectionInfo Subclass-specific data for making a peer-to-peer connection between
     * master and slave.
     * @return the app, if successfully started. Otherwise returns null.
     */
    protected abstract App2D startSlave(String connectionInfo, FirstVisibleInitializer fvi);

    /**
     * Message receiver
     */
    private class AppConventionalMessageReceiver
            implements ChannelComponent.ComponentMessageReceiver
    {
        public void messageReceived(CellMessage message) {
            if (message instanceof AppConventionalCellSetConnectionInfoMessage) {
                handleConnectionInfo((AppConventionalCellSetConnectionInfoMessage) message);
            } else if (message instanceof AppConventionalCellAppExittedMessage) {
                handleAppExitted((AppConventionalCellAppExittedMessage) message);
            } else {
                logger.warning("Unexpected message type: " + message.getClass());
            }

        }
    }

    /** {@inheritDoc} */
    @Override
    public void setApp(App2D app) throws IllegalArgumentException, IllegalStateException {
        // Same as super, but doesn't move fvi into the app. This is done earlier in
        // a conventional app
        if (app == null) {
            throw new IllegalArgumentException("Argument app is null");
        }
        if (this.app != null) {
            throw new IllegalStateException("Cell already has an app");
        }

        this.app = app;
        setName(app.getName());
    }
}
TOP

Related Classes of org.jdesktop.wonderland.modules.appbase.client.cell.AppConventionalCell$AppConventionalMessageReceiver

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.