// // $Id$ // // Narya library - tools for developing networked games // Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved // http://www.threerings.net/code/narya/ // // This library is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published // by the Free Software Foundation; either version 2.1 of the License, or // (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package com.threerings.crowd.server; import java.util.Iterator; import java.util.List; import com.samskivert.util.HashIntMap; import com.threerings.presents.dobj.RootDObjectManager; import com.threerings.presents.server.InvocationException; import com.threerings.presents.server.InvocationManager; import com.threerings.crowd.data.CrowdCodes; import com.threerings.crowd.data.Place; import com.threerings.crowd.data.PlaceConfig; import com.threerings.crowd.data.PlaceObject; import static com.threerings.crowd.Log.log; /** * The place registry keeps track of all of the active places in the server. It should be used to * create new places and it will take care of instantiating and initializing a place manager to * manage newly created places. */ public class PlaceRegistry { /** Used in conjunction with {@link #createPlace}. */ public static interface PreStartupHook { public void invoke (PlaceManager plmgr); } /** The location provider used by the place registry to provide location-related invocation * services. */ public LocationProvider locprov; /** * Creates and initializes the place registry. This is called by the server during its * initialization phase. */ public PlaceRegistry (InvocationManager invmgr, RootDObjectManager omgr) { // create and register our location provider locprov = new LocationProvider(invmgr, omgr, this); invmgr.registerDispatcher(new LocationDispatcher(locprov), CrowdCodes.CROWD_GROUP); // we'll need these later _omgr = omgr; _invmgr = invmgr; } /** * By overriding this method, it is possible to customize the place registry to cause it to * load the classes associated with a particular place via a custom class loader. That loader * may enforce restricted privileges or obtain the classes from some special source. * * @return the class loader to use when instantiating the {@link PlaceManager} associated with * the supplied {@link PlaceConfig}. This method must not return null. */ public ClassLoader getClassLoader (PlaceConfig config) { return getClass().getClassLoader(); } /** * Creates and registers a new place manager with no delegates. * * @see #createPlace(PlaceConfig,List) */ public PlaceManager createPlace (PlaceConfig config) throws InstantiationException, InvocationException { return createPlace(config, null, null); } /** * Creates and registers a new place manager along with the place object to be managed. The * registry takes care of tracking the creation of the object and informing the manager when it * is created. * * @param config the configuration object for the place to be created. The {@link PlaceManager} * derived class that should be instantiated to manage the place will be determined from the * config object. * @param delegates a list of {@link PlaceManagerDelegate} instances to be registered with the * manager prior to it being initialized and started up. * * @return a reference to the place manager, which will have been configured with its place * object and started up (via a call to {@link PlaceManager#startup}. * * @exception InstantiationException thrown if an error occurs trying to instantiate and * initialize the place manager. * @exception InvocationException thrown if the place manager returns failure from the call to * {@link PlaceManager#checkPermissions}. The error string returned by that call will be * provided as in the exception. */ public PlaceManager createPlace (PlaceConfig config, List delegates) throws InstantiationException, InvocationException { return createPlace(config, delegates, null); } /** * Don't use this method, see {@link #createPlace(PlaceConfig)}. * * @param hook an optional pre-startup hook that allows a place manager to be configured prior * to having {@link PlaceManager#startup} called. This mainly exists because it used to be * possible to do such things. Try not to use this in new code. */ public PlaceManager createPlace (PlaceConfig config, PreStartupHook hook) throws InstantiationException, InvocationException { return createPlace(config, null, hook); } /** * Returns the place manager associated with the specified place object id or null if no such * place exists. */ public PlaceManager getPlaceManager (int placeOid) { return _pmgrs.get(placeOid); } /** * Returns an enumeration of all of the registered place objects. This should only be accessed * on the dobjmgr thread and shouldn't be kept around across event dispatches. */ public Iterator enumeratePlaces () { final Iterator itr = _pmgrs.values().iterator(); return new Iterator() { public boolean hasNext (){ return itr.hasNext(); } public PlaceObject next () { PlaceManager plmgr = itr.next(); return (plmgr == null) ? null : plmgr.getPlaceObject(); } public void remove () { throw new UnsupportedOperationException(); } }; } /** * Returns an enumeration of all of the registered place managers. This should only be * accessed on the dobjmgr thread and shouldn't be kept around across event dispatches. */ public Iterator enumeratePlaceManagers () { return _pmgrs.values().iterator(); } /** * Creates a place manager using the supplied config, adds the supplied list of delegates, runs * the supplied pre-startup hook and finally returns it. */ protected PlaceManager createPlace (PlaceConfig config, List delegates, PreStartupHook hook) throws InstantiationException, InvocationException { PlaceManager pmgr = null; ClassLoader loader = getClassLoader(config); try { // create a place manager using the class supplied in the place config pmgr = (PlaceManager)Class.forName( config.getManagerClassName(), true, loader).newInstance(); // if we have delegates, add them if (delegates != null) { for (PlaceManagerDelegate delegate : delegates) { pmgr.addDelegate(delegate); } } // let the pmgr know about us and its configuration pmgr.init(this, _invmgr, _omgr, config); } catch (Exception e) { log.warning(e); throw new InstantiationException("Error creating PlaceManager for " + config); } // give the manager an opportunity to abort the whole process if it fails any permissions // checks String errmsg = pmgr.checkPermissions(); if (errmsg != null) { // give the place manager a chance to clean up after its early initialization process pmgr.permissionsFailed(); throw new InvocationException(errmsg); } // and create and register the place object PlaceObject plobj = pmgr.createPlaceObject(); _omgr.registerObject(plobj); // stick the manager into our table _pmgrs.put(plobj.getOid(), pmgr); // start the place manager up with the newly created place object try { if (hook != null) { hook.invoke(pmgr); } pmgr.startup(plobj); } catch (Exception e) { log.warning("Error starting place manager [obj=" + plobj + ", pmgr=" + pmgr + "].", e); } return pmgr; } /** * Called by the place manager when it has been shut down. */ protected void unmapPlaceManager (PlaceManager pmgr) { int ploid = pmgr.getPlaceObject().getOid(); // remove it from the table if (_pmgrs.remove(ploid) == null) { log.warning("Requested to unmap unmapped place manager [pmgr=" + pmgr + "]."); // } else { // Log.info("Unmapped place manager [class=" + pmgr.getClass().getName() + // ", ploid=" + ploid + "]."); } } /** The invocation manager with which we operate. */ protected InvocationManager _invmgr; /** The distributed object manager with which we operate. */ protected RootDObjectManager _omgr; /** A mapping from place object id to place manager. */ protected HashIntMap _pmgrs = new HashIntMap(); }