Beginnings of runtime control panel stuff.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@1428 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -0,0 +1,38 @@
|
|||||||
|
//
|
||||||
|
// $Id: Log.java,v 1.1 2002/06/07 06:22:24 mdb Exp $
|
||||||
|
|
||||||
|
package com.threerings.admin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A placeholder class that contains a reference to the log object used by
|
||||||
|
* the admin services.
|
||||||
|
*/
|
||||||
|
public class Log
|
||||||
|
{
|
||||||
|
public static com.samskivert.util.Log log =
|
||||||
|
new com.samskivert.util.Log("admin");
|
||||||
|
|
||||||
|
/** Convenience function. */
|
||||||
|
public static void debug (String message)
|
||||||
|
{
|
||||||
|
log.debug(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convenience function. */
|
||||||
|
public static void info (String message)
|
||||||
|
{
|
||||||
|
log.info(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convenience function. */
|
||||||
|
public static void warning (String message)
|
||||||
|
{
|
||||||
|
log.warning(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convenience function. */
|
||||||
|
public static void logStackTrace (Throwable t)
|
||||||
|
{
|
||||||
|
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// $Id: AdminService.java,v 1.1 2002/06/07 06:22:24 mdb Exp $
|
||||||
|
|
||||||
|
package com.threerings.admin.client;
|
||||||
|
|
||||||
|
import com.threerings.presents.client.Client;
|
||||||
|
import com.threerings.presents.client.InvocationDirector;
|
||||||
|
|
||||||
|
import com.threerings.admin.Log;
|
||||||
|
import com.threerings.admin.data.AdminCodes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the client side of the admin invocation services.
|
||||||
|
*/
|
||||||
|
public class AdminService
|
||||||
|
implements AdminCodes
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Requests the list of config objects. This will result in a call to
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public void handleConfigInfoResponse (int invid, String[] keys,
|
||||||
|
* int[] oids)
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* on the response target.
|
||||||
|
*/
|
||||||
|
public static void getConfigInfo (Client client, Object rsptarget)
|
||||||
|
{
|
||||||
|
InvocationDirector invdir = client.getInvocationDirector();
|
||||||
|
invdir.invoke(MODULE_NAME, GET_CONFIG_INFO_REQUEST, null, rsptarget);
|
||||||
|
Log.debug("Sent getConfigInfo request.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
//
|
||||||
|
// $Id: ConfigEditorPanel.java,v 1.1 2002/06/07 06:22:24 mdb Exp $
|
||||||
|
|
||||||
|
package com.threerings.admin.client;
|
||||||
|
|
||||||
|
import javax.swing.JTabbedPane;
|
||||||
|
import javax.swing.event.AncestorEvent;
|
||||||
|
|
||||||
|
import com.samskivert.swing.event.AncestorAdapter;
|
||||||
|
import com.samskivert.util.StringUtil;
|
||||||
|
|
||||||
|
import com.threerings.media.SafeScrollPane;
|
||||||
|
import com.threerings.presents.util.PresentsContext;
|
||||||
|
|
||||||
|
import com.threerings.admin.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches a list of the configuration objects in use by the server and
|
||||||
|
* displays their fields in a tree widget to be viewed and edited.
|
||||||
|
*/
|
||||||
|
public class ConfigEditorPanel extends JTabbedPane
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Constructs an editor panel which will use the supplied context to
|
||||||
|
* access the distributed object services.
|
||||||
|
*/
|
||||||
|
public ConfigEditorPanel (PresentsContext ctx)
|
||||||
|
{
|
||||||
|
_ctx = ctx;
|
||||||
|
|
||||||
|
// when we're hidden, we want to clear out our subscriptions
|
||||||
|
addAncestorListener(new AncestorAdapter () {
|
||||||
|
public void ancestorRemoved (AncestorEvent event) {
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// if we have no children, ship off a getConfigInfo request to
|
||||||
|
// find out what config objects are available for editing
|
||||||
|
if (getComponentCount() == 0) {
|
||||||
|
Log.info("Sending get config info.");
|
||||||
|
AdminService.getConfigInfo(_ctx.getClient(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called in response to our getConfigInfo server-side service
|
||||||
|
* request.
|
||||||
|
*/
|
||||||
|
public void handleConfigInfo (int invid, String[] keys, int[] oids)
|
||||||
|
{
|
||||||
|
Log.info("Got config info: " + StringUtil.toString(keys));
|
||||||
|
// create object editor panels for each of the categories
|
||||||
|
for (int ii = 0; ii < keys.length; ii++) {
|
||||||
|
ObjectEditorPanel panel =
|
||||||
|
new ObjectEditorPanel(_ctx, keys[ii], oids[ii]);
|
||||||
|
SafeScrollPane scrolly = new SafeScrollPane(panel);
|
||||||
|
addTab(keys[ii], scrolly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the panel is hidden; this instructs all of our object
|
||||||
|
* editors to clear out their subscriptions.
|
||||||
|
*/
|
||||||
|
protected void cleanup ()
|
||||||
|
{
|
||||||
|
int ccount = getComponentCount();
|
||||||
|
for (int ii = 0; ii < ccount; ii++) {
|
||||||
|
SafeScrollPane scrolly = (SafeScrollPane)getComponent(ii);
|
||||||
|
ObjectEditorPanel opanel = (ObjectEditorPanel)
|
||||||
|
scrolly.getViewport().getView();
|
||||||
|
opanel.cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Our client context. */
|
||||||
|
protected PresentsContext _ctx;
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
//
|
||||||
|
// $Id: FieldEditor.java,v 1.1 2002/06/07 06:22:24 mdb Exp $
|
||||||
|
|
||||||
|
package com.threerings.admin.client;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
import javax.swing.BorderFactory;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
|
||||||
|
import com.samskivert.swing.HGroupLayout;
|
||||||
|
|
||||||
|
import com.threerings.presents.dobj.AttributeChangeListener;
|
||||||
|
import com.threerings.presents.dobj.AttributeChangedEvent;
|
||||||
|
import com.threerings.presents.dobj.DObject;
|
||||||
|
import com.threerings.presents.util.PresentsContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to display and edit a particular distributed object field.
|
||||||
|
*/
|
||||||
|
public class FieldEditor extends JPanel
|
||||||
|
implements AttributeChangeListener, ActionListener
|
||||||
|
{
|
||||||
|
public FieldEditor (PresentsContext ctx, Field field, DObject object)
|
||||||
|
{
|
||||||
|
_ctx = ctx;
|
||||||
|
_field = field;
|
||||||
|
_object = object;
|
||||||
|
|
||||||
|
// create our interface elements
|
||||||
|
setLayout(new HGroupLayout(HGroupLayout.STRETCH));
|
||||||
|
|
||||||
|
// set up our default border
|
||||||
|
updateBorder(false);
|
||||||
|
|
||||||
|
// a label to display the field name
|
||||||
|
add(_label = new JLabel(_field.getName()), HGroupLayout.FIXED);
|
||||||
|
|
||||||
|
// and a text entry field to display the field value
|
||||||
|
String value = "";
|
||||||
|
try {
|
||||||
|
value = String.valueOf(_field.get(_object));
|
||||||
|
} catch (IllegalAccessException iae) {
|
||||||
|
value = "<error>";
|
||||||
|
}
|
||||||
|
add(_value = new JTextField(value));
|
||||||
|
_value.addActionListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public void attributeChanged (AttributeChangedEvent event)
|
||||||
|
{
|
||||||
|
_value.setText(String.valueOf(event.getValue()));
|
||||||
|
updateBorder(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public void actionPerformed (ActionEvent event)
|
||||||
|
{
|
||||||
|
if (_field.getType().equals(Integer.class) ||
|
||||||
|
_field.getType().equals(Integer.TYPE)) {
|
||||||
|
try {
|
||||||
|
Integer value = new Integer(_value.getText());
|
||||||
|
AttributeChangedEvent ace = new AttributeChangedEvent(
|
||||||
|
_object.getOid(), _field.getName(), value);
|
||||||
|
_ctx.getDObjectManager().postEvent(ace);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
updateBorder(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the appropriate border on this field editor based on whether
|
||||||
|
* or not the field is modified.
|
||||||
|
*/
|
||||||
|
protected void updateBorder (boolean modified)
|
||||||
|
{
|
||||||
|
if (modified) {
|
||||||
|
setBorder(BorderFactory.createMatteBorder(2, 2, 2, 2, Color.red));
|
||||||
|
} else {
|
||||||
|
setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PresentsContext _ctx;
|
||||||
|
protected Field _field;
|
||||||
|
protected DObject _object;
|
||||||
|
|
||||||
|
protected JLabel _label;
|
||||||
|
protected JTextField _value;
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
//
|
||||||
|
// $Id: ObjectEditorPanel.java,v 1.1 2002/06/07 06:22:24 mdb Exp $
|
||||||
|
|
||||||
|
package com.threerings.admin.client;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
|
import javax.swing.BorderFactory;
|
||||||
|
import javax.swing.event.AncestorEvent;
|
||||||
|
|
||||||
|
import com.samskivert.swing.VGroupLayout;
|
||||||
|
import com.samskivert.swing.ScrollablePanel;
|
||||||
|
import com.samskivert.swing.event.AncestorAdapter;
|
||||||
|
|
||||||
|
import com.threerings.presents.dobj.DObject;
|
||||||
|
import com.threerings.presents.dobj.ObjectAccessException;
|
||||||
|
import com.threerings.presents.dobj.Subscriber;
|
||||||
|
import com.threerings.presents.util.PresentsContext;
|
||||||
|
|
||||||
|
import com.threerings.admin.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to edit the distributed object fields of a particular
|
||||||
|
* configuration object. When the panel is first shown, it will subscribe
|
||||||
|
* to the object and display its fields. It will not automatically
|
||||||
|
* unsubscribe when it is hidden, but rather {@link #cleanup} must be
|
||||||
|
* called to let it know that it's not going to be shown again soon and it
|
||||||
|
* is safe for it to clear out its subscription.
|
||||||
|
*
|
||||||
|
* @see ConfigEditorPanel
|
||||||
|
*/
|
||||||
|
public class ObjectEditorPanel extends ScrollablePanel
|
||||||
|
implements Subscriber
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Creates an object editor panel for the specified configuration
|
||||||
|
* object.
|
||||||
|
*/
|
||||||
|
public ObjectEditorPanel (PresentsContext ctx, String key, int oid)
|
||||||
|
{
|
||||||
|
super(new VGroupLayout(VGroupLayout.NONE, VGroupLayout.STRETCH,
|
||||||
|
5, VGroupLayout.TOP));
|
||||||
|
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||||
|
|
||||||
|
// keep this business around
|
||||||
|
_ctx = ctx;
|
||||||
|
_key = key;
|
||||||
|
_oid = oid;
|
||||||
|
|
||||||
|
// when we're hidden, we want to clear out our subscriptions
|
||||||
|
addAncestorListener(new AncestorAdapter () {
|
||||||
|
public void ancestorAdded (AncestorEvent event) {
|
||||||
|
// subscribe to our object the first time we're shown
|
||||||
|
if (_object == null) {
|
||||||
|
_ctx.getDObjectManager().subscribeToObject(
|
||||||
|
_oid, ObjectEditorPanel.this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public boolean getScrollableTracksViewportWidth ()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method must be called to let the object editor panel know that
|
||||||
|
* it's OK for it to remove its subscription to its config object.
|
||||||
|
*/
|
||||||
|
public void cleanup ()
|
||||||
|
{
|
||||||
|
if (_object != null) {
|
||||||
|
_ctx.getDObjectManager().unsubscribeFromObject(_oid, this);
|
||||||
|
_object = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear out our field editors
|
||||||
|
removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public void objectAvailable (DObject object)
|
||||||
|
{
|
||||||
|
// keep this for later
|
||||||
|
_object = object;
|
||||||
|
|
||||||
|
// create our field editors
|
||||||
|
try {
|
||||||
|
Field[] fields = object.getClass().getFields();
|
||||||
|
for (int ii = 0; ii < fields.length; ii++) {
|
||||||
|
// if the field is anything but a plain old public field,
|
||||||
|
// we don't want to edit it
|
||||||
|
if (fields[ii].getModifiers() == Modifier.PUBLIC) {
|
||||||
|
add(new FieldEditor(_ctx, fields[ii], _object));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (SecurityException se) {
|
||||||
|
Log.warning("Unable to introspect DObject!? " + se);
|
||||||
|
}
|
||||||
|
|
||||||
|
revalidate();
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public void requestFailed (int oid, ObjectAccessException cause)
|
||||||
|
{
|
||||||
|
Log.warning("Unable to subscribe to config object: " + cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PresentsContext _ctx;
|
||||||
|
protected String _key;
|
||||||
|
protected int _oid;
|
||||||
|
protected DObject _object;
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// $Id: AdminCodes.java,v 1.1 2002/06/07 06:22:24 mdb Exp $
|
||||||
|
|
||||||
|
package com.threerings.admin.data;
|
||||||
|
|
||||||
|
import com.threerings.presents.data.InvocationCodes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains codes used by the admin invocation services.
|
||||||
|
*/
|
||||||
|
public interface AdminCodes extends InvocationCodes
|
||||||
|
{
|
||||||
|
/** The module name for the admin services. */
|
||||||
|
public static final String MODULE_NAME = "admin";
|
||||||
|
|
||||||
|
/** The message identifier for a getConfigInfo request. */
|
||||||
|
public static final String GET_CONFIG_INFO_REQUEST = "GetConfigInfo";
|
||||||
|
|
||||||
|
/** The response identifier for a successful getConfigInfo request. */
|
||||||
|
public static final String CONFIG_INFO_RESPONSE = "ConfigInfo";
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
//
|
||||||
|
// $Id: AdminProvider.java,v 1.1 2002/06/07 06:22:24 mdb Exp $
|
||||||
|
|
||||||
|
package com.threerings.admin.server;
|
||||||
|
|
||||||
|
import com.threerings.presents.server.InvocationManager;
|
||||||
|
import com.threerings.presents.server.InvocationProvider;
|
||||||
|
import com.threerings.presents.server.ServiceFailedException;
|
||||||
|
|
||||||
|
import com.threerings.crowd.data.BodyObject;
|
||||||
|
|
||||||
|
import com.threerings.admin.Log;
|
||||||
|
import com.threerings.admin.data.AdminCodes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the server-side interface to various administrator services.
|
||||||
|
*/
|
||||||
|
public class AdminProvider extends InvocationProvider
|
||||||
|
implements AdminCodes
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Constructs an admin provider and registers it with the invocation
|
||||||
|
* manager to handle admin services. This must be called by any server
|
||||||
|
* that wishes to make use of the admin services.
|
||||||
|
*/
|
||||||
|
public static void init (InvocationManager invmgr)
|
||||||
|
{
|
||||||
|
invmgr.registerProvider(MODULE_NAME, new AdminProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes a request from a client to obtain the configuration keys
|
||||||
|
* and oids for all registered configuration objects.
|
||||||
|
*/
|
||||||
|
public void handleGetConfigInfoRequest (BodyObject source, int invid)
|
||||||
|
throws ServiceFailedException
|
||||||
|
{
|
||||||
|
// we don't have to validate the request because the user can't do
|
||||||
|
// anything with the keys or oids unless they're an admin (we put
|
||||||
|
// the burden of doing that checking on the creator of the config
|
||||||
|
// object because we would otherwise need some mechanism to
|
||||||
|
// determine whether a user is an admin and we don't want to force
|
||||||
|
// some primitive system on the service user)
|
||||||
|
String[] keys = ConfObjRegistry.getKeys();
|
||||||
|
int[] oids = new int[keys.length];
|
||||||
|
for (int ii = 0; ii < keys.length; ii++) {
|
||||||
|
oids[ii] = ConfObjRegistry.getObject(keys[ii]).getOid();
|
||||||
|
}
|
||||||
|
sendResponse(source, invid, CONFIG_INFO_RESPONSE, keys, oids);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
//
|
||||||
|
// $Id: ConfObjRegistry.java,v 1.1 2002/06/07 06:22:24 mdb Exp $
|
||||||
|
|
||||||
|
package com.threerings.admin.server;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import com.threerings.presents.dobj.AccessController;
|
||||||
|
import com.threerings.presents.dobj.DObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a registry of configuration distributed objects. Using
|
||||||
|
* distributed object to store runtime configuration data can be
|
||||||
|
* exceptionally useful in that clients (with admin privileges) can view
|
||||||
|
* and update the running server's configuration parameters on the fly.
|
||||||
|
*
|
||||||
|
* <p> Users of the service are responsible for creating their own
|
||||||
|
* configuration objects which are then registered with this class which
|
||||||
|
* simply makes available a list of all registered configuration objects
|
||||||
|
* to the client. The service users will want to use {@link
|
||||||
|
* AccessController}s on their configuration distributed objects to
|
||||||
|
* prevent non-administrators from subscribing to or modifying the
|
||||||
|
* objects.
|
||||||
|
*/
|
||||||
|
public class ConfObjRegistry
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Registers the supplied configuration object with the system.
|
||||||
|
*
|
||||||
|
* @param key a string that identifies this object. These are
|
||||||
|
* generally hierarchical in nature (of the form
|
||||||
|
* <code>system.subsystem</code>), for example:
|
||||||
|
* <code>yohoho.crew</code>.
|
||||||
|
* @param object the object to be registered.
|
||||||
|
*/
|
||||||
|
public static void registerObject (String key, DObject object)
|
||||||
|
{
|
||||||
|
_confobjs.put(key, object);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the config object mapped to the specified key, or null if
|
||||||
|
* none exists for that key.
|
||||||
|
*/
|
||||||
|
public static DObject getObject (String key)
|
||||||
|
{
|
||||||
|
return (DObject)_confobjs.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array containing the keys of all registered
|
||||||
|
* configuration objects.
|
||||||
|
*/
|
||||||
|
public static String[] getKeys ()
|
||||||
|
{
|
||||||
|
String[] keys = new String[_confobjs.size()];
|
||||||
|
Iterator iter = _confobjs.keySet().iterator();
|
||||||
|
for (int ii = 0; iter.hasNext(); ii++) {
|
||||||
|
keys[ii] = (String)iter.next();
|
||||||
|
}
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A mapping from identifying key to config object. */
|
||||||
|
protected static HashMap _confobjs = new HashMap();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user