//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 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 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.Log;
import com.threerings.crowd.data.CrowdCodes;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.crowd.data.PlaceObject;
/**
* 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; 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 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.
*
* @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)
throws InstantiationException, InvocationException
{
return createPlace(config, 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
{
PlaceManager pmgr = null;
ClassLoader loader = getClassLoader(config);
try {
// load up the manager class
Class pmgrClass = Class.forName(
config.getManagerClassName(), true, loader);
// create a place manager for this place
pmgr = (PlaceManager)pmgrClass.newInstance();
// let the pmgr know about us and its configuration
pmgr.init(this, _invmgr, _omgr, config);
} catch (Exception e) {
Log.logStackTrace(e);
throw new InstantiationException(
"Error creating place manager [config=" + 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 + "].");
Log.logStackTrace(e);
}
return pmgr;
}
/**
* 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();
}
/**
* 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();
}