Created a database backed implementation of the runtime config services.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3870 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -205,6 +205,7 @@
|
|||||||
<fileset dir="${classes.dir}" includes="com/threerings/presents/**"/>
|
<fileset dir="${classes.dir}" includes="com/threerings/presents/**"/>
|
||||||
<fileset dir="${classes.dir}" includes="com/threerings/crowd/**"/>
|
<fileset dir="${classes.dir}" includes="com/threerings/crowd/**"/>
|
||||||
<fileset dir="${classes.dir}" includes="com/threerings/admin/**"/>
|
<fileset dir="${classes.dir}" includes="com/threerings/admin/**"/>
|
||||||
|
<fileset dir="src/sql" includes="admin/*.sql"/>
|
||||||
<fileset dir="${classes.dir}" includes="rsrc/config/presents/**"/>
|
<fileset dir="${classes.dir}" includes="rsrc/config/presents/**"/>
|
||||||
<fileset dir="${classes.dir}" includes="rsrc/config/crowd/**"/>
|
<fileset dir="${classes.dir}" includes="rsrc/config/crowd/**"/>
|
||||||
</jar>
|
</jar>
|
||||||
|
|||||||
@@ -21,8 +21,31 @@
|
|||||||
|
|
||||||
package com.threerings.admin.server;
|
package com.threerings.admin.server;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import com.samskivert.io.ByteArrayOutInputStream;
|
||||||
|
import com.samskivert.util.StringUtil;
|
||||||
|
|
||||||
|
import com.threerings.io.ObjectInputStream;
|
||||||
|
import com.threerings.io.ObjectOutputStream;
|
||||||
|
import com.threerings.io.Streamable;
|
||||||
|
|
||||||
import com.threerings.presents.dobj.AccessController;
|
import com.threerings.presents.dobj.AccessController;
|
||||||
|
import com.threerings.presents.dobj.AttributeChangeListener;
|
||||||
|
import com.threerings.presents.dobj.AttributeChangedEvent;
|
||||||
import com.threerings.presents.dobj.DObject;
|
import com.threerings.presents.dobj.DObject;
|
||||||
|
import com.threerings.presents.dobj.DSet;
|
||||||
|
import com.threerings.presents.dobj.EntryAddedEvent;
|
||||||
|
import com.threerings.presents.dobj.EntryRemovedEvent;
|
||||||
|
import com.threerings.presents.dobj.EntryUpdatedEvent;
|
||||||
|
import com.threerings.presents.dobj.ObjectAccessException;
|
||||||
|
import com.threerings.presents.dobj.SetListener;
|
||||||
|
|
||||||
|
import com.threerings.admin.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a registry of configuration distributed objects. Using distributed
|
* Provides a registry of configuration distributed objects. Using distributed
|
||||||
@@ -57,20 +80,286 @@ public abstract class ConfigRegistry
|
|||||||
* hierarchical in nature (of the form <code>system.subsystem</code>), for
|
* hierarchical in nature (of the form <code>system.subsystem</code>), for
|
||||||
* example: <code>yohoho.crew</code>.
|
* example: <code>yohoho.crew</code>.
|
||||||
* @param path The the path in the persistent configuration repository.
|
* @param path The the path in the persistent configuration repository.
|
||||||
|
* This may mean something to the underlying persistent store, for example
|
||||||
|
* in the preferences backed implementation it defines the path to the
|
||||||
|
* preferences node in the package hierarchy.
|
||||||
* @param object the object to be registered.
|
* @param object the object to be registered.
|
||||||
*/
|
*/
|
||||||
public abstract void registerObject (
|
public void registerObject (String key, String path, DObject object)
|
||||||
String key, String path, DObject object);
|
{
|
||||||
|
ObjectRecord record = createObjectRecord(path, object);
|
||||||
|
record.init();
|
||||||
|
_configs.put(key, record);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the config object mapped to the specified key, or null if none
|
* Returns the config object mapped to the specified key, or null if none
|
||||||
* exists for that key.
|
* exists for that key.
|
||||||
*/
|
*/
|
||||||
public abstract DObject getObject (String key);
|
public DObject getObject (String key)
|
||||||
|
{
|
||||||
|
ObjectRecord record = (ObjectRecord)_configs.get(key);
|
||||||
|
return (record == null) ? null : record.object;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array containing the keys of all registered configuration
|
* Returns an array containing the keys of all registered configuration
|
||||||
* objects.
|
* objects.
|
||||||
*/
|
*/
|
||||||
public abstract String[] getKeys ();
|
public String[] getKeys ()
|
||||||
|
{
|
||||||
|
return (String[])_configs.keySet().toArray(new String[_configs.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an object record derivation that will handle the management of
|
||||||
|
* the specified object.
|
||||||
|
*/
|
||||||
|
protected abstract ObjectRecord createObjectRecord (
|
||||||
|
String path, DObject object);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains all necessary info for a configuration object registration.
|
||||||
|
*/
|
||||||
|
protected abstract class ObjectRecord
|
||||||
|
implements AttributeChangeListener, SetListener
|
||||||
|
{
|
||||||
|
public DObject object;
|
||||||
|
|
||||||
|
public ObjectRecord (DObject object)
|
||||||
|
{
|
||||||
|
this.object = object;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init ()
|
||||||
|
{
|
||||||
|
// read in the initial configuration settings from the persistent
|
||||||
|
// configuration repository
|
||||||
|
Class cclass = object.getClass();
|
||||||
|
try {
|
||||||
|
Field[] fields = cclass.getFields();
|
||||||
|
for (int ii = 0; ii < fields.length; ii++) {
|
||||||
|
int mods = fields[ii].getModifiers();
|
||||||
|
if ((mods & Modifier.STATIC) != 0 ||
|
||||||
|
(mods & Modifier.PUBLIC) == 0 ||
|
||||||
|
(mods & Modifier.TRANSIENT) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
initField(fields[ii]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// listen for attribute updates
|
||||||
|
object.addListener(this);
|
||||||
|
|
||||||
|
} catch (SecurityException se) {
|
||||||
|
Log.warning("Unable to reflect on " + cclass.getName() + ": " +
|
||||||
|
se + ". Refusing to monitor object.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void entryAdded (EntryAddedEvent event)
|
||||||
|
{
|
||||||
|
serializeAttribute(event.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void entryUpdated (EntryUpdatedEvent event)
|
||||||
|
{
|
||||||
|
serializeAttribute(event.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void entryRemoved (EntryRemovedEvent event)
|
||||||
|
{
|
||||||
|
serializeAttribute(event.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void attributeChanged (AttributeChangedEvent event)
|
||||||
|
{
|
||||||
|
// mirror this configuration update to the persistent config
|
||||||
|
String key = StringUtil.unStudlyName(event.getName());
|
||||||
|
Object value = event.getValue();
|
||||||
|
if (value instanceof Boolean) {
|
||||||
|
setValue(key, ((Boolean)value).booleanValue());
|
||||||
|
} else if (value instanceof Short) {
|
||||||
|
setValue(key, ((Short)value).shortValue());
|
||||||
|
} else if (value instanceof Integer) {
|
||||||
|
setValue(key, ((Integer)value).intValue());
|
||||||
|
} else if (value instanceof Long) {
|
||||||
|
setValue(key, ((Long)value).longValue());
|
||||||
|
} else if (value instanceof Float) {
|
||||||
|
setValue(key, ((Float)value).floatValue());
|
||||||
|
} else if (value instanceof String) {
|
||||||
|
setValue(key, (String)value);
|
||||||
|
} else if (value instanceof float[]) {
|
||||||
|
setValue(key, (float[])value);
|
||||||
|
} else if (value instanceof int[]) {
|
||||||
|
setValue(key, (int[])value);
|
||||||
|
} else if (value instanceof String[]) {
|
||||||
|
setValue(key, (String[])value);
|
||||||
|
} else if (value instanceof long[]) {
|
||||||
|
setValue(key, (long[]) value);
|
||||||
|
} else if (value instanceof DSet) {
|
||||||
|
serializeAttribute(event.getName());
|
||||||
|
} else {
|
||||||
|
Log.info("Unable to flush config object change " +
|
||||||
|
"[cobj=" + object.getClass().getName() +
|
||||||
|
", key=" + key +
|
||||||
|
", type=" + value.getClass().getName() +
|
||||||
|
", value=" + value + "].");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Initializes a single field of a config distributed object from
|
||||||
|
* its corresponding value in the associated config repository. */
|
||||||
|
protected void initField (Field field)
|
||||||
|
{
|
||||||
|
String key = StringUtil.unStudlyName(field.getName());
|
||||||
|
Class type = field.getType();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (type.equals(Boolean.TYPE)) {
|
||||||
|
boolean defval = field.getBoolean(object);
|
||||||
|
field.setBoolean(object, getValue(key, defval));
|
||||||
|
|
||||||
|
} else if (type.equals(Short.TYPE)) {
|
||||||
|
short defval = field.getShort(object);
|
||||||
|
field.setShort(object, getValue(key, defval));
|
||||||
|
|
||||||
|
} else if (type.equals(Integer.TYPE)) {
|
||||||
|
int defval = field.getInt(object);
|
||||||
|
field.setInt(object, getValue(key, defval));
|
||||||
|
|
||||||
|
} else if (type.equals(Long.TYPE)) {
|
||||||
|
long defval = field.getLong(object);
|
||||||
|
field.setLong(object, getValue(key, defval));
|
||||||
|
|
||||||
|
} else if (type.equals(Float.TYPE)) {
|
||||||
|
float defval = field.getFloat(object);
|
||||||
|
field.setFloat(object, getValue(key, defval));
|
||||||
|
|
||||||
|
} else if (type.equals(String.class)) {
|
||||||
|
String defval = (String)field.get(object);
|
||||||
|
field.set(object, getValue(key, defval));
|
||||||
|
|
||||||
|
} else if (type.equals(INT_ARRAY_PROTO.getClass())) {
|
||||||
|
int[] defval = (int[])field.get(object);
|
||||||
|
field.set(object, getValue(key, defval));
|
||||||
|
|
||||||
|
} else if (type.equals(FLOAT_ARRAY_PROTO.getClass())) {
|
||||||
|
float[] defval = (float[])field.get(object);
|
||||||
|
field.set(object, getValue(key, defval));
|
||||||
|
|
||||||
|
} else if (type.equals(STRING_ARRAY_PROTO.getClass())) {
|
||||||
|
String[] defval = (String[])field.get(object);
|
||||||
|
field.set(object, getValue(key, defval));
|
||||||
|
|
||||||
|
} else if (type.equals(LONG_ARRAY_PROTO.getClass())) {
|
||||||
|
long[] defval = (long[])field.get(object);
|
||||||
|
field.set(object, getValue(key, defval));
|
||||||
|
|
||||||
|
} else if (Streamable.class.isAssignableFrom(type)) {
|
||||||
|
// don't freak out if the conf is blank.
|
||||||
|
String value = getValue(key, "");
|
||||||
|
if (StringUtil.isBlank(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ByteArrayInputStream bin = new ByteArrayInputStream(
|
||||||
|
StringUtil.unhexlate(value));
|
||||||
|
ObjectInputStream oin = new ObjectInputStream(bin);
|
||||||
|
field.set(object, oin.readObject());
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.warning("Failure decoding config value [type=" +
|
||||||
|
type + ", field=" + field + ", exception=" +
|
||||||
|
e + "].");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log.warning("Can't init field of unknown type " +
|
||||||
|
"[cobj=" + object.getClass().getName() +
|
||||||
|
", key=" + key +
|
||||||
|
", type=" + type.getName() + "].");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IllegalAccessException iae) {
|
||||||
|
Log.warning("Can't set field " +
|
||||||
|
"[cobj=" + object.getClass().getName() +
|
||||||
|
", key=" + key + ", error=" + iae + "].");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the specified attribute from the configuration object, and
|
||||||
|
* serialize it.
|
||||||
|
*/
|
||||||
|
protected void serializeAttribute (String attributeName)
|
||||||
|
{
|
||||||
|
String key = StringUtil.unStudlyName(attributeName);
|
||||||
|
Object value;
|
||||||
|
try {
|
||||||
|
value = object.getAttribute(attributeName);
|
||||||
|
} catch (ObjectAccessException oae) {
|
||||||
|
Log.warning("Exception getting field [name=" + attributeName +
|
||||||
|
"exception=" + oae + "].");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof Streamable) {
|
||||||
|
serialize(key, value);
|
||||||
|
} else {
|
||||||
|
Log.info("Unable to flush config object change " +
|
||||||
|
"[cobj=" + object.getClass().getName() +
|
||||||
|
", key=" + key +
|
||||||
|
", type=" + value.getClass().getName() +
|
||||||
|
", value=" + value + "].");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the specified object as serialized data associated with
|
||||||
|
* the specified key.
|
||||||
|
*/
|
||||||
|
protected void serialize (String key, Object value)
|
||||||
|
{
|
||||||
|
ByteArrayOutInputStream out = new ByteArrayOutInputStream();
|
||||||
|
ObjectOutputStream oout = new ObjectOutputStream(out);
|
||||||
|
try {
|
||||||
|
oout.writeObject(value);
|
||||||
|
oout.flush();
|
||||||
|
setValue(key, StringUtil.hexlate(out.toByteArray()));
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
Log.info("Error serializing value " + value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract boolean getValue (String field, boolean defval);
|
||||||
|
protected abstract short getValue (String field, short defval);
|
||||||
|
protected abstract int getValue (String field, int defval);
|
||||||
|
protected abstract long getValue (String field, long defval);
|
||||||
|
protected abstract float getValue (String field, float defval);
|
||||||
|
protected abstract String getValue (String field, String defval);
|
||||||
|
protected abstract int[] getValue (String field, int[] defval);
|
||||||
|
protected abstract float[] getValue (String field, float[] defval);
|
||||||
|
protected abstract long[] getValue (String field, long[] defval);
|
||||||
|
protected abstract String[] getValue (String field, String[] defval);
|
||||||
|
|
||||||
|
protected abstract void setValue (String field, boolean value);
|
||||||
|
protected abstract void setValue (String field, short value);
|
||||||
|
protected abstract void setValue (String field, int value);
|
||||||
|
protected abstract void setValue (String field, long value);
|
||||||
|
protected abstract void setValue (String field, float value);
|
||||||
|
protected abstract void setValue (String field, String value);
|
||||||
|
protected abstract void setValue (String field, int[] value);
|
||||||
|
protected abstract void setValue (String field, float[] value);
|
||||||
|
protected abstract void setValue (String field, long[] value);
|
||||||
|
protected abstract void setValue (String field, String[] value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A mapping from identifying key to config object. */
|
||||||
|
protected HashMap _configs = new HashMap();
|
||||||
|
|
||||||
|
protected static final int[] INT_ARRAY_PROTO = new int[0];
|
||||||
|
protected static final float[] FLOAT_ARRAY_PROTO = new float[0];
|
||||||
|
protected static final String[] STRING_ARRAY_PROTO = new String[0];
|
||||||
|
protected static final long[] LONG_ARRAY_PROTO = new long[0];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,269 @@
|
|||||||
|
//
|
||||||
|
// $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.admin.server;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import com.samskivert.io.PersistenceException;
|
||||||
|
import com.samskivert.jdbc.ConnectionProvider;
|
||||||
|
import com.samskivert.util.Invoker;
|
||||||
|
import com.samskivert.util.StringUtil;
|
||||||
|
|
||||||
|
import com.threerings.presents.dobj.DObject;
|
||||||
|
|
||||||
|
import com.threerings.admin.Log;
|
||||||
|
import com.threerings.admin.server.persist.ConfigRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the {@link ConfigRegistry} using a JDBC database as a persistent
|
||||||
|
* store for the configuration information.
|
||||||
|
*/
|
||||||
|
public class DatabaseConfigRegistry extends ConfigRegistry
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Creates a configuration registry and prepares it for operation.
|
||||||
|
*
|
||||||
|
* @param conprov will provide access to our JDBC database.
|
||||||
|
* @param invoker this will be used to perform all database activity so as
|
||||||
|
* to avoid blocking the distributed object thread.
|
||||||
|
*/
|
||||||
|
public DatabaseConfigRegistry (ConnectionProvider conprov, Invoker invoker)
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
_repo = new ConfigRepository(conprov);
|
||||||
|
_invoker = invoker;
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited
|
||||||
|
protected ObjectRecord createObjectRecord (String path, DObject object)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class DatabaseObjectRecord extends ObjectRecord
|
||||||
|
{
|
||||||
|
public DatabaseObjectRecord (String path, DObject object)
|
||||||
|
{
|
||||||
|
super(object);
|
||||||
|
_path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init ()
|
||||||
|
{
|
||||||
|
// load up our persistent data and then allow the normal
|
||||||
|
// initialization process to take place
|
||||||
|
_invoker.postUnit(new Invoker.Unit() {
|
||||||
|
public boolean invoke () {
|
||||||
|
try {
|
||||||
|
_data = _repo.loadConfig(_path);
|
||||||
|
} catch (PersistenceException pe) {
|
||||||
|
Log.warning("Failed to load object configuration " +
|
||||||
|
"[path=" + _path + "].");
|
||||||
|
Log.logStackTrace(pe);
|
||||||
|
_data = new HashMap();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleResult () {
|
||||||
|
DatabaseObjectRecord.super.init();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean getValue (String field, boolean defval) {
|
||||||
|
String value = (String)_data.get(field);
|
||||||
|
try {
|
||||||
|
if (value != null) {
|
||||||
|
return Boolean.parseBoolean(value);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore bogus values and return the default
|
||||||
|
}
|
||||||
|
return defval;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected short getValue (String field, short defval) {
|
||||||
|
String value = (String)_data.get(field);
|
||||||
|
try {
|
||||||
|
if (value != null) {
|
||||||
|
return Short.parseShort(value);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore bogus values and return the default
|
||||||
|
}
|
||||||
|
return defval;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getValue (String field, int defval) {
|
||||||
|
String value = (String)_data.get(field);
|
||||||
|
try {
|
||||||
|
if (value != null) {
|
||||||
|
return Integer.parseInt(value);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore bogus values and return the default
|
||||||
|
}
|
||||||
|
return defval;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long getValue (String field, long defval) {
|
||||||
|
String value = (String)_data.get(field);
|
||||||
|
try {
|
||||||
|
if (value != null) {
|
||||||
|
return Long.parseLong(value);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore bogus values and return the default
|
||||||
|
}
|
||||||
|
return defval;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected float getValue (String field, float defval) {
|
||||||
|
String value = (String)_data.get(field);
|
||||||
|
try {
|
||||||
|
if (value != null) {
|
||||||
|
return Float.parseFloat(value);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore bogus values and return the default
|
||||||
|
}
|
||||||
|
return defval;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getValue (String field, String defval) {
|
||||||
|
String value = (String)_data.get(field);
|
||||||
|
return (value == null) ? defval : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int[] getValue (String field, int[] defval) {
|
||||||
|
String value = (String)_data.get(field);
|
||||||
|
try {
|
||||||
|
if (value != null) {
|
||||||
|
int[] avalue = StringUtil.parseIntArray(value);
|
||||||
|
if (avalue != null) {
|
||||||
|
return avalue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore bogus values and return the default
|
||||||
|
}
|
||||||
|
return defval;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected float[] getValue (String field, float[] defval) {
|
||||||
|
String value = (String)_data.get(field);
|
||||||
|
try {
|
||||||
|
if (value != null) {
|
||||||
|
float[] avalue = StringUtil.parseFloatArray(value);
|
||||||
|
if (avalue != null) {
|
||||||
|
return avalue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore bogus values and return the default
|
||||||
|
}
|
||||||
|
return defval;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long[] getValue (String field, long[] defval) {
|
||||||
|
String value = (String)_data.get(field);
|
||||||
|
try {
|
||||||
|
if (value != null) {
|
||||||
|
long[] avalue = StringUtil.parseLongArray(value);
|
||||||
|
if (avalue != null) {
|
||||||
|
return avalue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore bogus values and return the default
|
||||||
|
}
|
||||||
|
return defval;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String[] getValue (String field, String[] defval) {
|
||||||
|
String value = (String)_data.get(field);
|
||||||
|
try {
|
||||||
|
if (value != null) {
|
||||||
|
return StringUtil.parseStringArray(value);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore bogus values and return the default
|
||||||
|
}
|
||||||
|
return defval;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setValue (String field, boolean value) {
|
||||||
|
setAndFlush(field, String.valueOf(value));
|
||||||
|
}
|
||||||
|
protected void setValue (String field, short value) {
|
||||||
|
setAndFlush(field, String.valueOf(value));
|
||||||
|
}
|
||||||
|
protected void setValue (String field, int value) {
|
||||||
|
setAndFlush(field, String.valueOf(value));
|
||||||
|
}
|
||||||
|
protected void setValue (String field, long value) {
|
||||||
|
setAndFlush(field, String.valueOf(value));
|
||||||
|
}
|
||||||
|
protected void setValue (String field, float value) {
|
||||||
|
setAndFlush(field, String.valueOf(value));
|
||||||
|
}
|
||||||
|
protected void setValue (String field, String value) {
|
||||||
|
setAndFlush(field, value);
|
||||||
|
}
|
||||||
|
protected void setValue (String field, int[] value) {
|
||||||
|
setAndFlush(field, StringUtil.toString(value, "", ""));
|
||||||
|
}
|
||||||
|
protected void setValue (String field, float[] value) {
|
||||||
|
setAndFlush(field, StringUtil.toString(value, "", ""));
|
||||||
|
}
|
||||||
|
protected void setValue (String field, long[] value) {
|
||||||
|
setAndFlush(field, StringUtil.toString(value, "", ""));
|
||||||
|
}
|
||||||
|
protected void setValue (String field, String[] value) {
|
||||||
|
setAndFlush(field, StringUtil.joinEscaped(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setAndFlush (final String field, final String value) {
|
||||||
|
_data.put(field, value);
|
||||||
|
_invoker.postUnit(new Invoker.Unit() {
|
||||||
|
public boolean invoke () {
|
||||||
|
try {
|
||||||
|
_repo.updateConfig(_path, field, value);
|
||||||
|
} catch (PersistenceException pe) {
|
||||||
|
Log.warning("Failed to update object configuration " +
|
||||||
|
"[path=" + _path + ", field=" + field +
|
||||||
|
", value=" + value + "].");
|
||||||
|
Log.logStackTrace(pe);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String _path;
|
||||||
|
protected HashMap _data;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ConfigRepository _repo;
|
||||||
|
protected Invoker _invoker;
|
||||||
|
}
|
||||||
@@ -21,34 +21,8 @@
|
|||||||
|
|
||||||
package com.threerings.admin.server;
|
package com.threerings.admin.server;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
import com.samskivert.io.ByteArrayOutInputStream;
|
|
||||||
import com.samskivert.util.Config;
|
import com.samskivert.util.Config;
|
||||||
import com.samskivert.util.StringUtil;
|
|
||||||
|
|
||||||
import com.threerings.io.ObjectInputStream;
|
|
||||||
import com.threerings.io.ObjectOutputStream;
|
|
||||||
import com.threerings.io.Streamable;
|
|
||||||
|
|
||||||
import com.threerings.presents.dobj.AccessController;
|
|
||||||
import com.threerings.presents.dobj.AttributeChangeListener;
|
|
||||||
import com.threerings.presents.dobj.AttributeChangedEvent;
|
|
||||||
import com.threerings.presents.dobj.DObject;
|
import com.threerings.presents.dobj.DObject;
|
||||||
import com.threerings.presents.dobj.DSet;
|
|
||||||
import com.threerings.presents.dobj.EntryAddedEvent;
|
|
||||||
import com.threerings.presents.dobj.EntryRemovedEvent;
|
|
||||||
import com.threerings.presents.dobj.EntryUpdatedEvent;
|
|
||||||
import com.threerings.presents.dobj.SetListener;
|
|
||||||
|
|
||||||
import com.threerings.admin.Log;
|
|
||||||
import com.threerings.presents.dobj.ObjectAccessException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the {@link ConfigRegistry} using the Java preferences system as a
|
* Implements the {@link ConfigRegistry} using the Java preferences system as a
|
||||||
@@ -58,280 +32,81 @@ import com.threerings.presents.dobj.ObjectAccessException;
|
|||||||
public class PrefsConfigRegistry extends ConfigRegistry
|
public class PrefsConfigRegistry extends ConfigRegistry
|
||||||
{
|
{
|
||||||
// documentation inherited
|
// documentation inherited
|
||||||
public void registerObject (String key, String path, DObject object)
|
protected ObjectRecord createObjectRecord (String path, DObject object)
|
||||||
{
|
{
|
||||||
// create a new config record for this object
|
return null;
|
||||||
_configs.put(key, new ConfObjRecord(path, object));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// documentation inherited
|
protected class PrefsObjectRecord extends ObjectRecord
|
||||||
public DObject getObject (String key)
|
|
||||||
{
|
{
|
||||||
ConfObjRecord record = (ConfObjRecord)_configs.get(key);
|
|
||||||
return (record == null) ? null : record.confObj;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public String[] getKeys ()
|
|
||||||
{
|
|
||||||
String[] keys = new String[_configs.size()];
|
|
||||||
Iterator iter = _configs.keySet().iterator();
|
|
||||||
for (int ii = 0; iter.hasNext(); ii++) {
|
|
||||||
keys[ii] = (String)iter.next();
|
|
||||||
}
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Contains all necessary info for a configuration object registration.
|
|
||||||
*/
|
|
||||||
protected static class ConfObjRecord
|
|
||||||
implements AttributeChangeListener, SetListener
|
|
||||||
{
|
|
||||||
public DObject confObj;
|
|
||||||
public Config config;
|
public Config config;
|
||||||
|
|
||||||
public ConfObjRecord (String path, DObject confObj)
|
public PrefsObjectRecord (String path, DObject object)
|
||||||
{
|
{
|
||||||
|
super(object);
|
||||||
this.config = new Config(path);
|
this.config = new Config(path);
|
||||||
this.confObj = confObj;
|
|
||||||
|
|
||||||
// read in the initial configuration settings from the persistent
|
|
||||||
// configuration repository
|
|
||||||
Class cclass = confObj.getClass();
|
|
||||||
try {
|
|
||||||
Field[] fields = cclass.getFields();
|
|
||||||
for (int ii = 0; ii < fields.length; ii++) {
|
|
||||||
int mods = fields[ii].getModifiers();
|
|
||||||
if ((mods & Modifier.STATIC) != 0 ||
|
|
||||||
(mods & Modifier.PUBLIC) == 0 ||
|
|
||||||
(mods & Modifier.TRANSIENT) != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
initField(fields[ii]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// listen for attribute updates
|
protected boolean getValue (String field, boolean defval) {
|
||||||
confObj.addListener(this);
|
return config.getValue(field, defval);
|
||||||
|
|
||||||
} catch (SecurityException se) {
|
|
||||||
Log.warning("Unable to reflect on " + cclass.getName() + ": " +
|
|
||||||
se + ". Refusing to monitor object.");
|
|
||||||
}
|
}
|
||||||
|
protected short getValue (String field, short defval) {
|
||||||
|
return (short)config.getValue(field, defval);
|
||||||
|
}
|
||||||
|
protected int getValue (String field, int defval) {
|
||||||
|
return config.getValue(field, defval);
|
||||||
|
}
|
||||||
|
protected long getValue (String field, long defval) {
|
||||||
|
return config.getValue(field, defval);
|
||||||
|
}
|
||||||
|
protected float getValue (String field, float defval) {
|
||||||
|
return config.getValue(field, defval);
|
||||||
|
}
|
||||||
|
protected String getValue (String field, String defval) {
|
||||||
|
return config.getValue(field, defval);
|
||||||
|
}
|
||||||
|
protected int[] getValue (String field, int[] defval) {
|
||||||
|
return config.getValue(field, defval);
|
||||||
|
}
|
||||||
|
protected float[] getValue (String field, float[] defval) {
|
||||||
|
return config.getValue(field, defval);
|
||||||
|
}
|
||||||
|
protected long[] getValue (String field, long[] defval) {
|
||||||
|
return config.getValue(field, defval);
|
||||||
|
}
|
||||||
|
protected String[] getValue (String field, String[] defval) {
|
||||||
|
return config.getValue(field, defval);
|
||||||
}
|
}
|
||||||
|
|
||||||
// documentation inherited
|
protected void setValue (String field, boolean value) {
|
||||||
public void entryAdded (EntryAddedEvent event)
|
config.setValue(field, value);
|
||||||
{
|
|
||||||
serializeAttribute(event.getName());
|
|
||||||
}
|
}
|
||||||
|
protected void setValue (String field, short value) {
|
||||||
// documentation inherited
|
config.setValue(field, value);
|
||||||
public void entryUpdated (EntryUpdatedEvent event)
|
|
||||||
{
|
|
||||||
serializeAttribute(event.getName());
|
|
||||||
}
|
}
|
||||||
|
protected void setValue (String field, int value) {
|
||||||
// documentation inherited
|
config.setValue(field, value);
|
||||||
public void entryRemoved (EntryRemovedEvent event)
|
|
||||||
{
|
|
||||||
serializeAttribute(event.getName());
|
|
||||||
}
|
}
|
||||||
|
protected void setValue (String field, long value) {
|
||||||
public void attributeChanged (AttributeChangedEvent event)
|
config.setValue(field, value);
|
||||||
{
|
}
|
||||||
// mirror this configuration update to the on-disk config
|
protected void setValue (String field, float value) {
|
||||||
String key = fieldToKey(event.getName());
|
config.setValue(field, value);
|
||||||
Object value = event.getValue();
|
}
|
||||||
|
protected void setValue (String field, String value) {
|
||||||
if (value instanceof Boolean) {
|
config.setValue(field, value);
|
||||||
config.setValue(key, ((Boolean)value).booleanValue());
|
}
|
||||||
} else if (value instanceof Short) {
|
protected void setValue (String field, int[] value) {
|
||||||
config.setValue(key, ((Short)value).shortValue());
|
config.setValue(field, value);
|
||||||
} else if (value instanceof Integer) {
|
}
|
||||||
config.setValue(key, ((Integer)value).intValue());
|
protected void setValue (String field, float[] value) {
|
||||||
} else if (value instanceof Long) {
|
config.setValue(field, value);
|
||||||
config.setValue(key, ((Long)value).longValue());
|
}
|
||||||
} else if (value instanceof Float) {
|
protected void setValue (String field, long[] value) {
|
||||||
config.setValue(key, ((Float)value).floatValue());
|
config.setValue(field, value);
|
||||||
} else if (value instanceof String) {
|
}
|
||||||
config.setValue(key, (String)value);
|
protected void setValue (String field, String[] value) {
|
||||||
} else if (value instanceof float[]) {
|
config.setValue(field, value);
|
||||||
config.setValue(key, (float[])value);
|
|
||||||
} else if (value instanceof int[]) {
|
|
||||||
config.setValue(key, (int[])value);
|
|
||||||
} else if (value instanceof String[]) {
|
|
||||||
config.setValue(key, (String[])value);
|
|
||||||
} else if (value instanceof long[]) {
|
|
||||||
config.setValue(key, (long[]) value);
|
|
||||||
} else if (value instanceof DSet) {
|
|
||||||
serializeAttribute(event.getName());
|
|
||||||
} else {
|
|
||||||
Log.info("Unable to flush config object change " +
|
|
||||||
"[cobj=" + confObj.getClass().getName() +
|
|
||||||
", key=" + key +
|
|
||||||
", type=" + value.getClass().getName() +
|
|
||||||
", value=" + value + "].");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Initializes a single field of a config distributed object from
|
|
||||||
* its corresponding value in the associated config repository. */
|
|
||||||
protected void initField (Field field)
|
|
||||||
{
|
|
||||||
String key = fieldToKey(field.getName());
|
|
||||||
Class type = field.getType();
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (type.equals(Boolean.TYPE)) {
|
|
||||||
boolean defval = field.getBoolean(confObj);
|
|
||||||
field.setBoolean(confObj, config.getValue(key, defval));
|
|
||||||
|
|
||||||
} else if (type.equals(Short.TYPE)) {
|
|
||||||
short defval = field.getShort(confObj);
|
|
||||||
defval = (short)config.getValue(key, defval);
|
|
||||||
field.setShort(confObj, defval);
|
|
||||||
|
|
||||||
} else if (type.equals(Integer.TYPE)) {
|
|
||||||
int defval = field.getInt(confObj);
|
|
||||||
field.setInt(confObj, config.getValue(key, defval));
|
|
||||||
|
|
||||||
} else if (type.equals(Long.TYPE)) {
|
|
||||||
long defval = field.getLong(confObj);
|
|
||||||
field.setLong(confObj, config.getValue(key, defval));
|
|
||||||
|
|
||||||
} else if (type.equals(Float.TYPE)) {
|
|
||||||
float defval = field.getFloat(confObj);
|
|
||||||
field.setFloat(confObj, config.getValue(key, defval));
|
|
||||||
|
|
||||||
} else if (type.equals(String.class)) {
|
|
||||||
String defval = (String)field.get(confObj);
|
|
||||||
field.set(confObj, config.getValue(key, defval));
|
|
||||||
|
|
||||||
} else if (type.equals(INT_ARRAY_PROTO.getClass())) {
|
|
||||||
int[] defval = (int[])field.get(confObj);
|
|
||||||
field.set(confObj, config.getValue(key, defval));
|
|
||||||
|
|
||||||
} else if (type.equals(FLOAT_ARRAY_PROTO.getClass())) {
|
|
||||||
float[] defval = (float[])field.get(confObj);
|
|
||||||
field.set(confObj, config.getValue(key, defval));
|
|
||||||
|
|
||||||
} else if (type.equals(STRING_ARRAY_PROTO.getClass())) {
|
|
||||||
String[] defval = (String[])field.get(confObj);
|
|
||||||
field.set(confObj, config.getValue(key, defval));
|
|
||||||
|
|
||||||
} else if (type.equals(LONG_ARRAY_PROTO.getClass())) {
|
|
||||||
long[] defval = (long[])field.get(confObj);
|
|
||||||
field.set(confObj, config.getValue(key, defval));
|
|
||||||
|
|
||||||
} else if (Streamable.class.isAssignableFrom(type)) {
|
|
||||||
// don't freak out if the conf is blank.
|
|
||||||
String value = config.getValue(key, "");
|
|
||||||
if (StringUtil.isBlank(value)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
ByteArrayInputStream bin = new ByteArrayInputStream(
|
|
||||||
StringUtil.unhexlate(value));
|
|
||||||
ObjectInputStream oin = new ObjectInputStream(bin);
|
|
||||||
field.set(confObj, oin.readObject());
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.warning("Failure decoding config value [type=" +
|
|
||||||
type + ", field=" + field + ", exception=" +
|
|
||||||
e + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Log.warning("Can't init field of unknown type " +
|
|
||||||
"[cobj=" + confObj.getClass().getName() +
|
|
||||||
", key=" + key +
|
|
||||||
", type=" + type.getName() + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IllegalAccessException iae) {
|
|
||||||
Log.warning("Can't set field " +
|
|
||||||
"[cobj=" + confObj.getClass().getName() +
|
|
||||||
", key=" + key + ", error=" + iae + "].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a mixed case field name (for example,
|
|
||||||
* <code>millisPerTick</code>) to a lower case, underscored key
|
|
||||||
* name (in this case, <code>millis_per_tick</code>).
|
|
||||||
*/
|
|
||||||
protected String fieldToKey (String fieldName)
|
|
||||||
{
|
|
||||||
StringBuffer key = new StringBuffer();
|
|
||||||
int flength = fieldName.length();
|
|
||||||
boolean seenLower = false;
|
|
||||||
for (int ii = 0; ii < flength; ii++) {
|
|
||||||
char c = fieldName.charAt(ii);
|
|
||||||
if (Character.isUpperCase(c)) {
|
|
||||||
if (seenLower) {
|
|
||||||
key.append("_");
|
|
||||||
}
|
|
||||||
key.append(Character.toLowerCase(c));
|
|
||||||
seenLower = false;
|
|
||||||
} else {
|
|
||||||
key.append(c);
|
|
||||||
seenLower = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return key.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the specified attribute from the configuration object, and
|
|
||||||
* serialize it.
|
|
||||||
*/
|
|
||||||
protected void serializeAttribute (String attributeName)
|
|
||||||
{
|
|
||||||
String key = fieldToKey(attributeName);
|
|
||||||
Object value;
|
|
||||||
try {
|
|
||||||
value = confObj.getAttribute(attributeName);
|
|
||||||
} catch (ObjectAccessException oae) {
|
|
||||||
Log.warning("Exception getting field [name=" + attributeName +
|
|
||||||
"exception=" + oae + "].");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value instanceof Streamable) {
|
|
||||||
serialize(key, value);
|
|
||||||
} else {
|
|
||||||
Log.info("Unable to flush config object change " +
|
|
||||||
"[cobj=" + confObj.getClass().getName() +
|
|
||||||
", key=" + key +
|
|
||||||
", type=" + value.getClass().getName() +
|
|
||||||
", value=" + value + "].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save the specified object as serialized data associated with
|
|
||||||
* the specified key.
|
|
||||||
*/
|
|
||||||
protected void serialize (String key, Object value)
|
|
||||||
{
|
|
||||||
ByteArrayOutInputStream out = new ByteArrayOutInputStream();
|
|
||||||
ObjectOutputStream oout = new ObjectOutputStream(out);
|
|
||||||
try {
|
|
||||||
oout.writeObject(value);
|
|
||||||
oout.flush();
|
|
||||||
config.setValue(key, StringUtil.hexlate(out.toByteArray()));
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
Log.info("Error serializing value " + value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static final int[] INT_ARRAY_PROTO = new int[0];
|
|
||||||
protected static final float[] FLOAT_ARRAY_PROTO = new float[0];
|
|
||||||
protected static final String[] STRING_ARRAY_PROTO = new String[0];
|
|
||||||
protected static final long[] LONG_ARRAY_PROTO = new long[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A mapping from identifying key to config object. */
|
|
||||||
protected HashMap _configs = new HashMap();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
//
|
||||||
|
// $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.admin.server.persist;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains a single datum of configuration information.
|
||||||
|
*/
|
||||||
|
public class ConfigDatum
|
||||||
|
{
|
||||||
|
public String object;
|
||||||
|
public String field;
|
||||||
|
public String value;
|
||||||
|
|
||||||
|
public String toString () {
|
||||||
|
return object + "." + field + "=" + value + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
//
|
||||||
|
// $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.admin.server.persist;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import com.samskivert.io.PersistenceException;
|
||||||
|
import com.samskivert.jdbc.ConnectionProvider;
|
||||||
|
import com.samskivert.jdbc.DatabaseLiaison;
|
||||||
|
import com.samskivert.jdbc.JDBCUtil;
|
||||||
|
import com.samskivert.jdbc.JORARepository;
|
||||||
|
import com.samskivert.jdbc.jora.Session;
|
||||||
|
import com.samskivert.jdbc.jora.Table;
|
||||||
|
|
||||||
|
import com.threerings.admin.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores configuration information in a database table.
|
||||||
|
*/
|
||||||
|
public class ConfigRepository extends JORARepository
|
||||||
|
{
|
||||||
|
/** The identifier used when establishing a database connection. */
|
||||||
|
public static final String CONFIG_DB_IDENT = "configdb";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new config repository with the specified connection
|
||||||
|
* provider.
|
||||||
|
*/
|
||||||
|
public ConfigRepository (ConnectionProvider conprov)
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
super(conprov, CONFIG_DB_IDENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads up the configuration data for the specified object.
|
||||||
|
*
|
||||||
|
* @return a map containing field/value pairs for all stored configuration
|
||||||
|
* data.
|
||||||
|
*/
|
||||||
|
public HashMap loadConfig (String object)
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
ArrayList list = loadAll(
|
||||||
|
_ctable, "where OBJECT = " + JDBCUtil.escape(object));
|
||||||
|
HashMap data = new HashMap();
|
||||||
|
for (int ii = 0, ll = list.size(); ii < ll; ii++) {
|
||||||
|
ConfigDatum datum = (ConfigDatum)list.get(ii);
|
||||||
|
data.put(datum.field, datum.value);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the specified configuration datum.
|
||||||
|
*/
|
||||||
|
public void updateConfig (String object, String field, String value)
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
ConfigDatum datum = new ConfigDatum();
|
||||||
|
datum.object = object;
|
||||||
|
datum.field = field;
|
||||||
|
datum.value = value;
|
||||||
|
store(_ctable, datum);
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited
|
||||||
|
protected void migrateSchema (Connection conn, DatabaseLiaison liaison)
|
||||||
|
throws SQLException, PersistenceException
|
||||||
|
{
|
||||||
|
if (!JDBCUtil.tableExists(conn, "CONFIG")) {
|
||||||
|
Log.info("Creating admin.config schema...");
|
||||||
|
JDBCUtil.loadSchema(conn, "admin/config.sql");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited
|
||||||
|
protected void createTables (Session session)
|
||||||
|
{
|
||||||
|
_ctable = new Table(
|
||||||
|
ConfigDatum.class.getName(), "CONFIG", session, "OBJECT", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Table _ctable;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* Schema for the Admin services configuration table.
|
||||||
|
*/
|
||||||
|
|
||||||
|
drop table if exists CONFIG;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains all registered configuration data.
|
||||||
|
*/
|
||||||
|
CREATE TABLE CONFIG
|
||||||
|
(
|
||||||
|
/** The configuration object with which this datum is associated. */
|
||||||
|
OBJECT VARCHAR(255) NOT NULL,
|
||||||
|
|
||||||
|
/** The configuration object field name. */
|
||||||
|
FIELD VARCHAR(255) NOT NULL,
|
||||||
|
|
||||||
|
/** The value of the object field. */
|
||||||
|
VALUE TEXT NOT NULL,
|
||||||
|
|
||||||
|
/** Defines our table keys. */
|
||||||
|
PRIMARY KEY (OBJECT, FIELD)
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user