Package org.jdesktop.wonderland.server.cell

Source Code of org.jdesktop.wonderland.server.cell.CellManagerMO

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

import com.sun.sgs.app.AppContext;
import com.sun.sgs.app.DataManager;
import com.sun.sgs.app.ManagedObject;
import com.sun.sgs.app.NameNotBoundException;
import com.sun.sgs.app.util.ScalableHashMap;
import com.sun.sgs.app.util.ScalableHashSet;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jdesktop.wonderland.common.ExperimentalAPI;
import org.jdesktop.wonderland.common.InternalAPI;
import org.jdesktop.wonderland.common.cell.CellID;
import org.jdesktop.wonderland.common.cell.MultipleParentException;
import org.jdesktop.wonderland.common.cell.state.CellComponentUtils;
import org.jdesktop.wonderland.server.WonderlandContext;
import org.jdesktop.wonderland.server.cell.view.AvatarCellMO;
import org.jdesktop.wonderland.server.comms.CommsManager;
import org.jdesktop.wonderland.server.spatial.CellMOListener;
import org.jdesktop.wonderland.server.spatial.UniverseManager;
import org.jdesktop.wonderland.server.spatial.UniverseManagerFactory;
import org.jdesktop.wonderland.server.wfs.exporter.CellExporter;
import org.jdesktop.wonderland.server.wfs.importer.CellImporter;

/**
*
* @author paulby
*/
@ExperimentalAPI
public class CellManagerMO implements ManagedObject, Serializable {
    private static final Logger logger =
            Logger.getLogger(CellManagerMO.class.getName());

    // bindings
    private static final String NAME = CellManagerMO.class.getName();
    private static final String COUNTER_BINDING_NAME = NAME + ".CellCounter";
    private static final String ROOTCELLS_BINDING_NAME = NAME + ".RootCells";
    private static final String COMPONENTS_BINDING_NAME = NAME + ".Components";
    private static final String ENV_CREATOR_BINDING_NAME = NAME + ".EnvCreator";

    /**
     * Creates a new instance of CellManagerMO.  Use the singleton
     * getCellManager() method instead.
     */
    CellManagerMO() {
    }
   
    /**
     * Initialize the master cell cache. This is an implementation detail and
     * should not be called by users of this class.
     */
    @InternalAPI
    public static void initialize() {
        logger.fine("CellManagerMO Initializing");

        // add a listener that will be notified of any cell creation
        // in the system
        UniverseManager universe = AppContext.getManager(UniverseManager.class);
        universe.addCellListener(new CellCreationListener());

        // register the cell channel message listener
        CommsManager cm = WonderlandContext.getCommsManager();
        cm.registerClientHandler(new CellChannelConnectionHandler());
       
        // Register the cell cache message handler
        cm.registerClientHandler(new CellCacheConnectionHandler());
       
        // Register the cell hierarchy edit message handler
        cm.registerClientHandler(new CellEditConnectionHandler());
    }

    /**
     * Return singleton master cell cache
     * @return the master cell cache
     */
    public static CellManagerMO getCellManager() {
        // return a unique instance of cell manager, since each instance
        // is stateless.  All calls that require state will look up the
        // appropriate bindings in the Darkstar datastore to get the
        // correct values for those states.  This removed a
        // heavily-contended object in favor of a few smaller objects.
        return new CellManagerMO();
    }
   
    /**
     * Return the cell with the given ID, or null if the id is invalid
     *
     * @param cellID the cell ID to getTranslation
     * @return the cell with the given ID
     */
    public static CellMO getCell(CellID cellID) {
        if (cellID.equals(CellID.getInvalidCellID()))
            return null;

        try {
            return (CellMO) AppContext.getDataManager().getBinding(getCellBinding(cellID));
        } catch(NameNotBoundException e) {
            return null;
        }
    }

    /**
     * Get the singleton environment cell
     * @return the environment cell
     */
    public static EnvironmentCellMO getEnvironmentCell() {
        return (EnvironmentCellMO) getCell(CellID.getEnvironmentCellID());
    }

    /**
     * Register to create the environment cell. This object will be called if
     * a WFS is loaded that does not define an environment cell.
     * @param creator the creator to register
     */
    public void registerEnvironmentCellCreator(EnvironmentCellCreator creator) {
        AppContext.getDataManager().setBinding(ENV_CREATOR_BINDING_NAME, creator);
    }

    /**
     * Create an environment cell using the default creator.
     * @param creator the cell creator
     */
    protected EnvironmentCellMO createEnvironmentCell() {
        DataManager dm = AppContext.getDataManager();
        EnvironmentCellCreator creator =
                (EnvironmentCellCreator) dm.getBinding(ENV_CREATOR_BINDING_NAME);
        EnvironmentCellMO env = creator.createEnvironmentCell();
       
        try {
            insertCellInWorld(env);
        } catch (MultipleParentException mpe) {
            // should never happen
            logger.log(Level.WARNING, "Error adding environment cell", mpe);
        }
       
        return env;
    }
   
    /**
     * Insert the cell into the world.
     */
    public void insertCellInWorld(CellMO cell) throws MultipleParentException {
        cell.setLive(true);

        if (cell instanceof EnvironmentCellMO) {
            // don't add environment cells to the world. Instead, just update
            // registrations
            new CellCreationListener().cellAdded(cell);
            return;
        }

        // add the cell to the universe
        UniverseManagerFactory.getUniverseManager().addRootToUniverse(cell);
        getRootCellsForUpdate().add(cell.cellID);
    }

    /**
     * Remove a cell from the world
     * @param cell the cell to remove
     */
    public void removeCellFromWorld(CellMO cell) {
        cell.setLive(false);
        // Set live now handles removal of root cells from the Universe
//        UniverseManagerFactory.getUniverseManager().removeRootFromUniverse(cell);
        getRootCellsForUpdate().remove(cell.getCellID());
    }

    /**
     * Get the list of all root cells in the world, creating it if it doesn't
     * exist
     * @return a set of root cells
     */
    public Set<CellID> getRootCells() {
        DataManager dm = AppContext.getDataManager();
        Set<CellID> out;
        try {
            out = (Set<CellID>) dm.getBinding(ROOTCELLS_BINDING_NAME);
        } catch (NameNotBoundException nnbe) {
            out = new ScalableHashSet<CellID>();
            dm.setBinding(ROOTCELLS_BINDING_NAME, out);
        }

        return out;
    }

    /**
     * Convenience method to mark the root cells for update
     * @return the root cells, marked for update
     */
    protected Set<CellID> getRootCellsForUpdate() {
        Set<CellID> out = getRootCells();
        AppContext.getDataManager().markForUpdate(out);
        return out;
    }

    /**
     * Load the initial world.  This will typically load the cells
     * from WFS
     */
    public void loadWorld() {
        new CellImporter().load();


        // make sure there is an environment cell. If there isn't, create one.
        if (getEnvironmentCell() == null) {
            createEnvironmentCell();
        }
    }

    public void saveWorld() {
        new CellExporter().export(null);
    }
   
    /**
     * Returns a unique cell id and registers the cell with the system
     * @return
     */
    CellID createCellID(CellMO cell) {
        DataManager dm = AppContext.getDataManager();
        CellID cellID;

        if (cell instanceof EnvironmentCellMO) {
            // special case: environment cell is a singleton that has a fixed id
            cellID = CellID.getEnvironmentCellID();
        } else {
            // default case: assign a new cell ID
            CellCounter counter;
            try {
                counter = (CellCounter) dm.getBindingForUpdate(COUNTER_BINDING_NAME);
            } catch (NameNotBoundException nnbe) {
                counter = new CellCounter();
                dm.setBinding(COUNTER_BINDING_NAME, counter);
            }

            cellID = new CellID(counter.nextCellID());
        }

        dm.setBinding(getCellBinding(cellID), cell);
        return cellID;
    }

    /**
     * Return a unique name for a cell, given its ID
     * @param ID the cell's ID
     */
    static String getCellBinding(CellID cellID) {
        return "CELL_" + cellID.toString();
    }

    /**
     * Register a component that will be added to avatar cells
     *
     * @param component
     */
    public void registerAvatarCellComponent(Class<? extends CellComponentMO> componentClass) {
        registerCellComponent(AvatarCellMO.class, componentClass);
    }

    /**
     * Register a component that will be automatically added to every cell
     * at creation time. Registrations will apply to subclasses as well,
     * so if you register a component to be added to CellMO.class, an instance
     * will be added to every cell at creation time.
     * @param cellClass the class of cell to register on
     * @param componentClass the class to instantiate
     */
    public void registerCellComponent(Class<? extends CellMO> cellClass,
                                      Class<? extends CellComponentMO> componentClass)
    {
        CellComponentMap cm = getComponentMap();
        AppContext.getDataManager().markForUpdate(cm);

        // add a record of this class to the map
        Set<Class<? extends CellComponentMO>> cms = cm.get(cellClass.getName());
        if (cms == null) {
            cms = new LinkedHashSet<Class<? extends CellComponentMO>>();
            cm.put(cellClass.getName(), cms);
        }

        cms.add(componentClass);
    }

    /**
     * Unregister a component type.
     * @param cellClass the class of cell to register on
     * @param componentClass the class to instantiate
     */
    public void unregisterCellComponent(Class<? extends CellMO> cellClass,
                                        Class<? extends CellComponentMO> componentClass)
    {
        CellComponentMap cm = getComponentMap();
        AppContext.getDataManager().markForUpdate(cm);

        Set<Class<? extends CellComponentMO>> cms = cm.get(cellClass.getName());
        if (cms != null) {
            cms.remove(componentClass);
        }
    }

    static Set<Class<? extends CellComponentMO>> getCellComponents(Class<? extends CellMO> cellClass) {
        CellComponentMap cm = getComponentMap();
        Set<Class<? extends CellComponentMO>> out = cm.get(cellClass.getName());
        if (out == null) {
            out = Collections.EMPTY_SET;
        }

        return out;
    }

    private static CellComponentMap getComponentMap() {
        DataManager dm = AppContext.getDataManager();
        CellComponentMap out;
        try {
            out = (CellComponentMap) dm.getBinding(COMPONENTS_BINDING_NAME);
        } catch (NameNotBoundException nnbe) {
            logger.log(Level.WARNING, COMPONENTS_BINDING_NAME + " not bound",
                       nnbe);
            out = new CellComponentMap();
            dm.setBinding(COMPONENTS_BINDING_NAME, out);
        }

        return out;
    }

    /**
     * Interface for the environment cell creator.
     */
    public interface EnvironmentCellCreator extends ManagedObject {
        public EnvironmentCellMO createEnvironmentCell();
    }

    private static final class CellCounter
            implements ManagedObject, Serializable
    {
        private long nextID = CellID.getFirstCellID();

        public long nextCellID() {
            return nextID++;
        }
    }

    /**
     * A listener that adds the cells from the component map to
     * the cell when the cell is created.
     */
    private static final class CellCreationListener
            implements CellMOListener, Serializable
    {
        public void cellAdded(CellMO cell) {
            // build the set of all components to add by looking at each
            // superclass of the cell, and adding all the components
            // registered on each superclass
            Set<Class<? extends CellComponentMO>> components =
                    new LinkedHashSet<Class<? extends CellComponentMO>>();

            // get the tree and add all classes
            Set<Class<? extends CellMO>> cellTree = getClassTree(cell.getClass());
            for (Class<? extends CellMO> cellClass : cellTree) {
                components.addAll(CellManagerMO.getCellComponents(cellClass));
            }

            // instantiate each component
            for(Class<? extends CellComponentMO> c : components) {
                // OWL issue #64: make sure component isn't a duplicate
                if (cell.getComponent(CellComponentUtils.getLookupClass(c)) != null) {
                    continue;
                }

                try {
                    Constructor con = c.getConstructor(CellMO.class);
                    CellComponentMO comp = (CellComponentMO) con.newInstance(cell);
                    cell.addComponent(comp);
                } catch (NoSuchMethodException ex) {
                    logger.log(Level.WARNING, null, ex);
                } catch (SecurityException ex) {
                    logger.log(Level.WARNING, null, ex);
                } catch (InstantiationException ex) {
                    logger.log(Level.WARNING, null, ex);
                } catch (IllegalAccessException ex) {
                    logger.log(Level.WARNING, null, ex);
                } catch (IllegalArgumentException ex) {
                    logger.log(Level.WARNING, null, ex);
                } catch (InvocationTargetException ex) {
                    // bug #527 -- rethrow Runtime exceptions
                    if (ex.getCause() != null &&
                            ex.getCause() instanceof RuntimeException)
                    {
                        throw (RuntimeException) ex.getCause();
                    }

                    logger.log(Level.WARNING, null, ex);
                }
            }
        }

        public void cellRemoved(CellMO cell) {
            // ignore
        }

        /**
         * Get all the superclasses of a cell up to CellMO
        */
        private static Set<Class<? extends CellMO>> getClassTree(Class<? extends CellMO> cellClass) {
            Set<Class<? extends CellMO>> classes =
                    new LinkedHashSet<Class<? extends CellMO>>();
            do {
                classes.add(cellClass);
                cellClass = (Class<? extends CellMO>) cellClass.getSuperclass();
            } while (cellClass != null && CellMO.class.isAssignableFrom(cellClass));

            return classes;
        }
    }

    /**
     * A map from class to the set of components registered for that class.
     * Note that we can't use class for the actual key, since the
     * hashCode of classes isn't stable across restarts.
     */
    private static class CellComponentMap
            extends ScalableHashMap<String,
                                    Set<Class<? extends CellComponentMO>>>
    {}
}
TOP

Related Classes of org.jdesktop.wonderland.server.cell.CellManagerMO

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.