diff --git a/src/java/com/threerings/crowd/Log.java b/src/java/com/threerings/crowd/Log.java index 29d02f271..5b21d130d 100644 --- a/src/java/com/threerings/crowd/Log.java +++ b/src/java/com/threerings/crowd/Log.java @@ -21,26 +21,22 @@ package com.threerings.crowd; +import java.util.logging.Level; +import java.util.logging.Logger; + /** - * A placeholder class that contains a reference to the log object used by - * the Crowd services. + * Contains a reference to the log object used by the Crowd services. */ public class Log { - public static com.samskivert.util.Log log = - new com.samskivert.util.Log("crowd"); - - /** Convenience function. */ - public static boolean debug () - { - return (com.samskivert.util.Log.getLevel() == - com.samskivert.util.Log.DEBUG); - } + /** We dispatch our log messages through this logger. */ + public static Logger log = + Logger.getLogger("com.threerings.narya.crowd"); /** Convenience function. */ public static void debug (String message) { - log.debug(message); + log.fine(message); } /** Convenience function. */ @@ -58,6 +54,6 @@ public class Log /** Convenience function. */ public static void logStackTrace (Throwable t) { - log.logStackTrace(com.samskivert.util.Log.WARNING, t); + log.log(Level.WARNING, t.getMessage(), t); } } diff --git a/src/java/com/threerings/crowd/data/PlaceObject.java b/src/java/com/threerings/crowd/data/PlaceObject.java index b7feefba2..98517abff 100644 --- a/src/java/com/threerings/crowd/data/PlaceObject.java +++ b/src/java/com/threerings/crowd/data/PlaceObject.java @@ -53,6 +53,22 @@ public class PlaceObject extends DObject public static final String SPEAK_SERVICE = "speakService"; // AUTO-GENERATED: FIELDS END + /** + * Exists solely to make calls into the manager look sensible: + *
_plobj.manager.invoke("someMethod", args);
+ */
+ public class ManagerCaller
+ {
+ public void invoke (String method, Object ... args) {
+ postMessage(method, args);
+ }
+ }
+
+ /**
+ * Allows the client to call methods on the manager.
+ */
+ public transient ManagerCaller manager = new ManagerCaller();
+
/**
* Tracks the oid of the body objects of all of the occupants of this
* place.
diff --git a/src/java/com/threerings/crowd/server/PlaceManager.java b/src/java/com/threerings/crowd/server/PlaceManager.java
index 108b474b8..dc34fad85 100644
--- a/src/java/com/threerings/crowd/server/PlaceManager.java
+++ b/src/java/com/threerings/crowd/server/PlaceManager.java
@@ -24,6 +24,7 @@ package com.threerings.crowd.server;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.logging.Level;
import com.samskivert.util.HashIntMap;
import com.samskivert.util.Interval;
@@ -36,8 +37,7 @@ import com.threerings.presents.server.PresentsDObjectMgr;
import com.threerings.presents.dobj.AccessController;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.DObjectManager;
-import com.threerings.presents.dobj.EntryAddedEvent;
-import com.threerings.presents.dobj.EntryRemovedEvent;
+import com.threerings.presents.dobj.DynamicEventDispatcher;
import com.threerings.presents.dobj.EntryUpdatedEvent;
import com.threerings.presents.dobj.MessageEvent;
import com.threerings.presents.dobj.MessageListener;
@@ -46,7 +46,7 @@ import com.threerings.presents.dobj.ObjectDeathListener;
import com.threerings.presents.dobj.ObjectDestroyedEvent;
import com.threerings.presents.dobj.ObjectRemovedEvent;
import com.threerings.presents.dobj.OidListListener;
-import com.threerings.presents.dobj.SetListener;
+import com.threerings.presents.dobj.SetAdapter;
import com.threerings.crowd.Log;
import com.threerings.crowd.data.BodyObject;
@@ -79,12 +79,15 @@ import com.threerings.crowd.chat.server.SpeakProvider;
*/
public class PlaceManager
implements MessageListener, OidListListener, ObjectDeathListener,
- SetListener, SpeakProvider.SpeakerValidator
+ SpeakProvider.SpeakerValidator
{
/**
- * An interface used to allow the registration of standard message
- * handlers to be invoked by the place manager when particular types
- * of message events are received.
+ * An interface used to allow the registration of standard message handlers
+ * to be invoked by the place manager when particular types of message
+ * events are received.
+ *
+ * @deprecated Use dynamically bound methods instead. See {@link
+ * DynamicEventDispatcher}.
*/
public static interface MessageHandler
{
@@ -160,27 +163,6 @@ public class PlaceManager
_plobj.updateOccupantInfo((OccupantInfo)occInfo.clone());
}
- /**
- * Derived classes will generally override this method to create a custom
- * {@link PlaceObject} derivation that contains extra information.
- */
- protected PlaceObject createPlaceObject ()
- {
- try {
- return getPlaceObjectClass().newInstance();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * @deprecated Use {@link #createPlaceObject}.
- */
- protected Class extends PlaceObject> getPlaceObjectClass ()
- {
- return PlaceObject.class;
- }
-
/**
* Called by the place registry after creating this place manager.
*/
@@ -241,15 +223,6 @@ public class PlaceManager
return null;
}
- /**
- * Called if the permissions check failed, to give place managers a
- * chance to do any cleanup that might be necessary due to their early
- * initialization or permissions checking code.
- */
- protected void permissionsFailed ()
- {
- }
-
/**
* Called by the place manager after the place object has been
* successfully created.
@@ -268,6 +241,7 @@ public class PlaceManager
// we'll need to hear about place object events
plobj.addListener(this);
+ plobj.addListener(_bodyUpdater);
// configure this place's access controller
plobj.setAccessController(getAccessController());
@@ -279,40 +253,6 @@ public class PlaceManager
checkShutdownInterval();
}
- /**
- * @return true if we should create a speaker service for our place object
- * so that clients can use it to speak in this place.
- */
- protected boolean shouldCreateSpeakService ()
- {
- return true;
- }
-
- /**
- * Creates an access controller for this place's distributed object, which
- * by default is {@link CrowdObjectAccess#PLACE}.
- */
- protected AccessController getAccessController ()
- {
- return CrowdObjectAccess.PLACE;
- }
-
- /**
- * Derived classes should override this (and be sure to call
- * super.didStartup()) to perform any startup time
- * initialization. The place object will be available by the time this
- * method is executed.
- */
- protected void didStartup ()
- {
- // let our delegates know that we've started up
- applyToDelegates(new DelegateOp() {
- public void apply (PlaceManagerDelegate delegate) {
- delegate.didStartup(_plobj);
- }
- });
- }
-
/**
* Causes the place object being managed by this place manager to be
* destroyed and the place manager to shut down.
@@ -331,22 +271,6 @@ public class PlaceManager
cancelShutdowner();
}
- /**
- * Called when this place has been destroyed and the place manager has
- * shut down (via a call to {@link #shutdown}). Derived classes can
- * override this method and perform any necessary shutdown time
- * processing.
- */
- protected void didShutdown ()
- {
- // let our delegates know that we've shut down
- applyToDelegates(new DelegateOp() {
- public void apply (PlaceManagerDelegate delegate) {
- delegate.didShutdown();
- }
- });
- }
-
/**
* Provides an opportunity for the place manager to prevent bodies from
* entering.
@@ -391,6 +315,87 @@ public class PlaceManager
}
}
+ /**
+ * Registers a particular message handler instance to be used when
+ * processing message events with the specified name.
+ *
+ * @param name the message name of the message events that should be
+ * handled by this handler.
+ * @param handler the handler to be registered.
+ *
+ * @deprecated Use dynamically bound methods instead. See {@link
+ * DynamicEventDispatcher}.
+ */
+ public void registerMessageHandler (String name, MessageHandler handler)
+ {
+ // create our handler map if necessary
+ if (_msghandlers == null) {
+ _msghandlers = new HashMapsuper.didStartup()) to perform any startup time
+ * initialization. The place object will be available by the time this
+ * method is executed.
+ */
+ protected void didStartup ()
+ {
+ // let our delegates know that we've started up
+ applyToDelegates(new DelegateOp() {
+ public void apply (PlaceManagerDelegate delegate) {
+ delegate.didStartup(_plobj);
+ }
+ });
+ }
+
+ /**
+ * Called when this place has been destroyed and the place manager has
+ * shut down (via a call to {@link #shutdown}). Derived classes can
+ * override this method and perform any necessary shutdown time
+ * processing.
+ */
+ protected void didShutdown ()
+ {
+ // let our delegates know that we've shut down
+ applyToDelegates(new DelegateOp() {
+ public void apply (PlaceManagerDelegate delegate) {
+ delegate.didShutdown();
+ }
+ });
+ }
+
/**
* Called when an occupant is being added to this place. This will be
* before the call to {@link #bodyEntered} and gives the derived class a
@@ -420,7 +521,7 @@ public class PlaceManager
*/
protected void bodyEntered (final int bodyOid)
{
- if (Log.debug()) {
+ if (Log.log.getLevel() == Level.FINE) {
Log.debug("Body entered [where=" + where() +
", oid=" + bodyOid + "].");
}
@@ -441,7 +542,7 @@ public class PlaceManager
*/
protected void bodyLeft (final int bodyOid)
{
- if (Log.debug()) {
+ if (Log.log.getLevel() == Level.FINE) {
Log.debug("Body left [where=" + where() +
", oid=" + bodyOid + "].");
}
@@ -551,122 +652,6 @@ public class PlaceManager
return 5 * 60 * 1000L;
}
- /**
- * Registers a particular message handler instance to be used when
- * processing message events with the specified name.
- *
- * @param name the message name of the message events that should be
- * handled by this handler.
- * @param handler the handler to be registered.
- */
- public void registerMessageHandler (String name, MessageHandler handler)
- {
- // create our handler map if necessary
- if (_msghandlers == null) {
- _msghandlers = new HashMapsuper.objectAdded if the event is one they don't
- * explicitly handle.
- */
- public void objectAdded (ObjectAddedEvent event)
- {
- if (event.getName().equals(PlaceObject.OCCUPANTS)) {
- bodyEntered(event.getOid());
- }
- }
-
- /**
- * Handles occupant departure from the place. Derived classes may need
- * to override this method to handle other oid lists in their derived
- * place objects. They should be sure to call
- * super.objectRemoved if the event is one they don't
- * explicitly handle.
- */
- public void objectRemoved (ObjectRemovedEvent event)
- {
- if (event.getName().equals(PlaceObject.OCCUPANTS)) {
- bodyLeft(event.getOid());
- }
- }
-
- /**
- * Handles place destruction. We shut ourselves down and ask the place
- * registry to unmap us.
- */
- public void objectDestroyed (ObjectDestroyedEvent event)
- {
- // unregister ourselves
- _registry.unmapPlaceManager(this);
-
- // let our derived classes and delegates shut themselves down
- didShutdown();
- }
-
- // documentation inherited from interface
- public void entryAdded (EntryAddedEvent event)
- {
- }
-
- // documentation inherited from interface
- public void entryUpdated (EntryUpdatedEvent event)
- {
- if (event.getName().equals(PlaceObject.OCCUPANT_INFO)) {
- bodyUpdated((OccupantInfo)event.getEntry());
- }
- }
-
- // documentation inherited from interface
- public void entryRemoved (EntryRemovedEvent event)
- {
- }
-
- // documentation inherited from interface
- public boolean isValidSpeaker (
- DObject speakObj, ClientObject speaker, byte mode)
- {
- // only allow people in the room to speak
- return _plobj.occupants.contains(speaker.getOid());
- }
-
- /**
- * Generates a string representation of this manager. Does so in a way
- * that makes it easier for derived classes to add to the string
- * representation.
- *
- * @see #toString(StringBuilder)
- */
- public String toString ()
- {
- StringBuilder buf = new StringBuilder();
- buf.append("[");
- toString(buf);
- buf.append("]");
- return buf.toString();
- }
-
/**
* An extensible way to add to the string representation of this
* class. Override this (being sure to call super) and append your
@@ -699,6 +684,15 @@ public class PlaceManager
}
}
+ /** Listens for occupant updates. */
+ protected SetAdapter _bodyUpdater = new SetAdapter() {
+ public void entryUpdated (EntryUpdatedEvent event) {
+ if (event.getName().equals(PlaceObject.OCCUPANT_INFO)) {
+ bodyUpdated((OccupantInfo)event.getEntry());
+ }
+ }
+ };
+
/** A reference to the place registry with which we're registered. */
protected PlaceRegistry _registry;
@@ -729,4 +723,9 @@ public class PlaceManager
* down after a certain period of idility, or null if no
* interval is currently registered. */
protected Interval _shutdownInterval;
+
+ /** We use this to do our method lookup magic when we receive message
+ * events. */
+ protected DynamicEventDispatcher _dispatcher =
+ new DynamicEventDispatcher(this);
}
diff --git a/src/java/com/threerings/presents/dobj/DynamicEventDispatcher.java b/src/java/com/threerings/presents/dobj/DynamicEventDispatcher.java
new file mode 100644
index 000000000..5cf86db59
--- /dev/null
+++ b/src/java/com/threerings/presents/dobj/DynamicEventDispatcher.java
@@ -0,0 +1,135 @@
+//
+// $Id$
+//
+// Narya library - tools for developing networked games
+// Copyright (C) 2002-2006 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.presents.dobj;
+
+import java.lang.reflect.Method;
+
+import java.util.HashMap;
+import java.util.logging.Level;
+
+import com.samskivert.util.MethodFinder;
+import com.samskivert.util.StringUtil;
+
+import static com.threerings.presents.Log.log;
+
+/**
+ * Maps distributed object events to methods using reflection.
+ */
+public class DynamicEventDispatcher
+ implements AttributeChangeListener, ElementUpdateListener, SetListener
+{
+ /**
+ * Creates a dynamic event dispatcher on the supplied target.
+ */
+ public DynamicEventDispatcher (Object target)
+ {
+ _target = target;
+ _finder = new MethodFinder(target.getClass());
+ }
+
+ // from interface AttributeChangeListener
+ public void attributeChanged (AttributeChangedEvent event)
+ {
+ dispatchMethod(event.getName() + "Changed",
+ new Object[] { event.getValue() });
+ }
+
+ // from interface ElementUpdateListener
+ public void elementUpdated (ElementUpdatedEvent event)
+ {
+ dispatchMethod(event.getName() + "Updated",
+ new Object[] { event.getIndex(), event.getValue() });
+ }
+
+ // from interface SetListener
+ public void entryAdded (EntryAddedEvent event)
+ {
+ dispatchMethod(event.getName() + "Added",
+ new Object[] { event.getEntry() });
+ }
+
+ // from interface SetListener
+ public void entryUpdated (EntryUpdatedEvent event)
+ {
+ dispatchMethod(event.getName() + "Updated",
+ new Object[] { event.getEntry() });
+ }
+
+ // from interface SetListener
+ public void entryRemoved (EntryRemovedEvent event)
+ {
+ dispatchMethod(event.getName() + "Removed",
+ new Object[] { event.getKey() });
+ }
+
+ /**
+ * Dynamically looks up the method in question on our target and dispatches
+ * an event if it does.
+ */
+ public void dispatchMethod (String name, Object[] arguments)
+ {
+ // first check the cache
+ Method method = _mcache.get(name);
+ if (method == null) {
+ // if we haven't already determined this method doesn't exist, try
+ // to resolve it
+ if (!_mcache.containsKey(name)) {
+ _mcache.put(name, method = resolveMethod(name, arguments));
+ }
+ }
+ if (method != null) {
+ try {
+ method.invoke(_target, arguments);
+ } catch (Exception e) {
+ log.log(Level.WARNING, "Failed to dispatch event callback " +
+ name + "(" + StringUtil.toString(arguments) + ").", e);
+ }
+ }
+ }
+
+ /**
+ * Looks for a method that matches the supplied signature.
+ */
+ protected Method resolveMethod (String name, Object[] arguments)
+ {
+ Class clazz = _target.getClass();
+ Class[] ptypes = new Class[arguments.length];
+ for (int ii = 0; ii < arguments.length; ii++) {
+ ptypes[ii] = arguments[ii] == null ?
+ Object.class : arguments[ii].getClass();
+ }
+ try {
+ return _finder.findMethod(name, ptypes);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /** The object on which we will dynamically dispatch events. */
+ protected Object _target;
+
+ /** Used to look up methods. */
+ protected MethodFinder _finder;
+
+ /** A cache of already resolved methods. */
+ protected HashMap