GWT frontend and servlet for editing runtime configuration.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6178 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
<module>
|
||||
<inherits name="com.threerings.web.Base"/>
|
||||
|
||||
<source path="client"/>
|
||||
<source path="gwt"/>
|
||||
</module>
|
||||
@@ -0,0 +1,72 @@
|
||||
//
|
||||
// $Id: $
|
||||
|
||||
package com.threerings.admin.web.client;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.rpc.ServiceDefTarget;
|
||||
import com.google.gwt.user.client.ui.TabPanel;
|
||||
|
||||
import com.threerings.admin.web.client.ConfigEditorTab.ConfigAccessor;
|
||||
import com.threerings.admin.web.gwt.ConfigField;
|
||||
import com.threerings.admin.web.gwt.ConfigService;
|
||||
import com.threerings.admin.web.gwt.ConfigService.ConfigurationRecord;
|
||||
import com.threerings.admin.web.gwt.ConfigService.ConfigurationResult;
|
||||
import com.threerings.admin.web.gwt.ConfigServiceAsync;
|
||||
import com.threerings.gwt.util.PopupCallback;
|
||||
|
||||
|
||||
/**
|
||||
* The main panel of the configuration editor. All service calls are routed through here.
|
||||
* Subclass this class in your project.
|
||||
*/
|
||||
public abstract class ConfigEditorPanel extends TabPanel
|
||||
implements ConfigAccessor
|
||||
{
|
||||
public ConfigEditorPanel ()
|
||||
{
|
||||
addStyleName("configEditorPanel");
|
||||
((ServiceDefTarget)_configsvc).setServiceEntryPoint(getServiceEntryPoint());
|
||||
_configsvc.getConfiguration(new PopupCallback<ConfigurationResult>() {
|
||||
public void onSuccess (ConfigurationResult result) {
|
||||
gotData(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void submitChanges (String key, ConfigField[] modified,
|
||||
AsyncCallback<ConfigurationRecord> callback)
|
||||
{
|
||||
_configsvc.updateConfiguration(key, modified, callback);
|
||||
}
|
||||
|
||||
protected void gotData (ConfigurationResult result)
|
||||
{
|
||||
clear();
|
||||
|
||||
if (result.records.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Entry<String, ConfigurationRecord> tab : result.records.entrySet()) {
|
||||
String tabKey = tab.getKey();
|
||||
ConfigEditorTab widget = new ConfigEditorTab(this, tabKey, tab.getValue());
|
||||
_tabs.put(tabKey, widget);
|
||||
add(widget, tabKey);
|
||||
}
|
||||
selectTab(0);
|
||||
}
|
||||
|
||||
/** Should return the absolute path of the servlet that implements {@link ConfigService}. */
|
||||
protected abstract String getServiceEntryPoint();
|
||||
|
||||
protected Map<String, ConfigEditorTab> _tabs = Maps.newHashMap();
|
||||
|
||||
protected static final ConfigServiceAsync _configsvc = GWT.create(ConfigService.class);
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
//
|
||||
// $Id: $
|
||||
|
||||
|
||||
package com.threerings.admin.web.client;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import com.google.gwt.user.client.Command;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
|
||||
import com.threerings.admin.web.gwt.ConfigField;
|
||||
import com.threerings.gwt.ui.InfoPopup;
|
||||
import com.threerings.gwt.ui.SmartTable;
|
||||
|
||||
import com.threerings.admin.web.gwt.ConfigService.ConfigurationRecord;
|
||||
|
||||
import com.threerings.gwt.util.ClickCallback;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ConfigEditorTab extends SmartTable
|
||||
{
|
||||
public interface ConfigAccessor
|
||||
{
|
||||
void submitChanges (String key, ConfigField[] modified,
|
||||
AsyncCallback<ConfigurationRecord> callback);
|
||||
}
|
||||
|
||||
public ConfigEditorTab (ConfigAccessor parent, String key, ConfigurationRecord record)
|
||||
{
|
||||
super("configEditorTab", 5, 5);
|
||||
|
||||
_parent = parent;
|
||||
_key = key;
|
||||
|
||||
_submit = new Button("Submit Changes");
|
||||
|
||||
// wire up saving the code on click
|
||||
new ClickCallback<ConfigurationRecord>(_submit) {
|
||||
protected boolean callService () {
|
||||
List<ConfigField> modified = Lists.newArrayList();
|
||||
for (ConfigFieldEditor editor : _editors) {
|
||||
ConfigField field = editor.getModifiedField();
|
||||
if (field != null) {
|
||||
modified.add(field);
|
||||
}
|
||||
}
|
||||
_parent.submitChanges(
|
||||
_key, modified.toArray(new ConfigField[modified.size()]), this);
|
||||
return true;
|
||||
}
|
||||
protected boolean gotResult (ConfigurationRecord result) {
|
||||
new InfoPopup("Updated " + result.updates + " fields.").show();
|
||||
updateTable(result);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
cell(1, 1).alignRight().widget(_submit);
|
||||
|
||||
updateTable(record);
|
||||
}
|
||||
|
||||
protected void updateTable (ConfigurationRecord record)
|
||||
{
|
||||
SmartTable table = new SmartTable(5, 5);
|
||||
table.setStyleName("configEditorTable");
|
||||
|
||||
int row = 0;
|
||||
for (ConfigField field : record.fields) {
|
||||
ConfigFieldEditor editor = ConfigFieldEditor.getEditorFor(field, UPDATE_BUTTON);
|
||||
_editors.add(editor);
|
||||
table.cell(row, 0).alignRight().widget(editor.getNameWidget());
|
||||
table.cell(row, 1).alignLeft().widget(editor.getValueWidget());
|
||||
table.cell(row, 2).alignLeft().widget(editor.getResetWidget());
|
||||
row ++;
|
||||
}
|
||||
|
||||
cell(0, 0).colSpan(2).widget(table);
|
||||
|
||||
UPDATE_BUTTON.execute();
|
||||
}
|
||||
|
||||
protected List<ConfigFieldEditor> _editors = Lists.newArrayList();
|
||||
|
||||
protected ConfigAccessor _parent;
|
||||
protected String _key;
|
||||
protected Button _submit;
|
||||
|
||||
protected Command UPDATE_BUTTON = new Command () {
|
||||
public void execute () {
|
||||
// search for any modified field; if found, enable submissions & exit
|
||||
for (ConfigFieldEditor editor : _editors) {
|
||||
if (editor.getModifiedField() != null) {
|
||||
_submit.setEnabled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
_submit.setEnabled(false);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
//
|
||||
// $Id: $
|
||||
|
||||
package com.threerings.admin.web.client;
|
||||
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.threerings.admin.web.gwt.ConfigField;
|
||||
import com.threerings.admin.web.gwt.ConfigField.FieldType;
|
||||
|
||||
import com.google.gwt.dom.client.Style;
|
||||
|
||||
import com.google.gwt.event.dom.client.ChangeEvent;
|
||||
import com.google.gwt.event.dom.client.ChangeHandler;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
|
||||
import com.google.gwt.event.logical.shared.ValueChangeEvent;
|
||||
import com.google.gwt.event.logical.shared.ValueChangeHandler;
|
||||
|
||||
import com.google.gwt.user.client.Command;
|
||||
import com.google.gwt.user.client.ui.CheckBox;
|
||||
import com.google.gwt.user.client.ui.TextBox;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
|
||||
/**
|
||||
* A class responsible for constructing and exporting the widgets involved in manipulating
|
||||
* one specific configuration field.
|
||||
*/
|
||||
public abstract class ConfigFieldEditor
|
||||
{
|
||||
public static ConfigFieldEditor getEditorFor (ConfigField field, Command onChange)
|
||||
{
|
||||
if (field.type == FieldType.BOOLEAN) {
|
||||
return new CheckboxFieldEditor(field, onChange);
|
||||
}
|
||||
return new StringFieldEditor(field, onChange);
|
||||
}
|
||||
|
||||
/**
|
||||
* This editor represents values as strings.
|
||||
*/
|
||||
protected static class StringFieldEditor extends ConfigFieldEditor
|
||||
{
|
||||
public StringFieldEditor (ConfigField field, Command onChange)
|
||||
{
|
||||
super(field, onChange);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Widget buildWidget (ConfigField field)
|
||||
{
|
||||
_box = new TextBox();
|
||||
_box.setStyleName("configStringEditor");
|
||||
_box.setVisibleLength(40);
|
||||
resetField();
|
||||
|
||||
_box.addChangeHandler(new ChangeHandler() {
|
||||
public void onChange (ChangeEvent changeEvent) {
|
||||
// if the string fails conversion, just reset to the old value
|
||||
if (_field.type.toValue(_box.getText().trim()) == null) {
|
||||
_box.setText(_field.valStr);
|
||||
}
|
||||
updateModificationState();
|
||||
}
|
||||
});
|
||||
return _box;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigField getModifiedField ()
|
||||
{
|
||||
Object newValue = _field.type.toValue(_box.getText().trim());
|
||||
if (newValue == null) {
|
||||
return null;
|
||||
}
|
||||
String newValStr = newValue.toString();
|
||||
if ((newValStr.isEmpty() && _field.valStr == null) ||newValStr.equals(_field.valStr)) {
|
||||
return null;
|
||||
}
|
||||
return new ConfigField(_field.name, _field.type, newValStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetField ()
|
||||
{
|
||||
_box.setText(_field.valStr);
|
||||
}
|
||||
|
||||
protected TextBox _box;
|
||||
}
|
||||
|
||||
/**
|
||||
* This editor represents boolean values as checkboxes.
|
||||
*/
|
||||
protected static class CheckboxFieldEditor extends ConfigFieldEditor
|
||||
{
|
||||
public CheckboxFieldEditor (ConfigField field, Command onChange)
|
||||
{
|
||||
super(field, onChange);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Widget buildWidget (ConfigField field)
|
||||
{
|
||||
_box = new CheckBox();
|
||||
_box.setStyleName("configCheckBoxEditor");
|
||||
resetField();
|
||||
|
||||
_box.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
|
||||
public void onValueChange (ValueChangeEvent changeEvent) {
|
||||
updateModificationState();
|
||||
}
|
||||
});
|
||||
return _box;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigField getModifiedField ()
|
||||
{
|
||||
String newValStr = Boolean.toString(_box.getValue());
|
||||
if (newValStr.equals(_field.valStr)) {
|
||||
return null;
|
||||
}
|
||||
return new ConfigField(_field.name, _field.type, newValStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetField ()
|
||||
{
|
||||
_box.setValue(Boolean.valueOf(_field.valStr));
|
||||
}
|
||||
|
||||
protected CheckBox _box;
|
||||
}
|
||||
|
||||
public ConfigFieldEditor (ConfigField field, Command onChange)
|
||||
{
|
||||
_field = field;
|
||||
_onChange = onChange;
|
||||
|
||||
_value = buildWidget(field);
|
||||
|
||||
_name = new Label(field.name);
|
||||
_name.setStyleName("fieldName");
|
||||
|
||||
_reset = new Label("X");
|
||||
_reset.setStyleName("resetButton");
|
||||
_reset.addClickHandler(new ClickHandler() {
|
||||
public void onClick (ClickEvent event) {
|
||||
resetField();
|
||||
updateModificationState();
|
||||
}
|
||||
});
|
||||
_reset.setVisible(false);
|
||||
}
|
||||
|
||||
protected void updateModificationState ()
|
||||
{
|
||||
Style style = _value.getElement().getStyle();
|
||||
if (getModifiedField() != null) {
|
||||
style.setBackgroundColor("red");
|
||||
_reset.setVisible(true);
|
||||
|
||||
} else {
|
||||
style.clearBackgroundColor();
|
||||
_reset.setVisible(false);
|
||||
}
|
||||
_onChange.execute();
|
||||
}
|
||||
|
||||
public Widget getNameWidget ()
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
public Widget getValueWidget ()
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
public Widget getResetWidget ()
|
||||
{
|
||||
return _reset;
|
||||
}
|
||||
|
||||
public abstract ConfigField getModifiedField ();
|
||||
|
||||
protected abstract Widget buildWidget (ConfigField field);
|
||||
protected abstract void resetField ();
|
||||
|
||||
protected ConfigField _field;
|
||||
protected Command _onChange;
|
||||
|
||||
protected Label _name, _reset;
|
||||
protected Widget _value;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
//
|
||||
// $Id: $
|
||||
|
||||
package com.threerings.admin.web.gwt;
|
||||
|
||||
import com.google.common.collect.ComparisonChain;
|
||||
|
||||
import com.google.gwt.user.client.rpc.IsSerializable;
|
||||
|
||||
/**
|
||||
* A GWT-friendly representation of a configuration tuple, consisting of the name of the entry,
|
||||
* a type enum, and the toString() of the value.
|
||||
*/
|
||||
public class ConfigField
|
||||
implements IsSerializable, Comparable<ConfigField>
|
||||
{
|
||||
public enum FieldType
|
||||
implements IsSerializable
|
||||
{
|
||||
INTEGER,
|
||||
SHORT,
|
||||
BYTE,
|
||||
LONG,
|
||||
FLOAT,
|
||||
BOOLEAN,
|
||||
DOUBLE,
|
||||
STRING;
|
||||
|
||||
/**
|
||||
* Create a string representation of the given value, which should be of the type
|
||||
* reflected in this enum.
|
||||
*/
|
||||
public String toString (Object value)
|
||||
{
|
||||
return (value != null) ? value.toString() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given string, which should have been created by {@link #toString(Object)},
|
||||
* back into its raw value form.
|
||||
*/
|
||||
public Object toValue (String text)
|
||||
{
|
||||
switch(this) {
|
||||
case INTEGER:
|
||||
return new Integer(text);
|
||||
case SHORT:
|
||||
return new Short(text);
|
||||
case BYTE:
|
||||
return new Byte(text);
|
||||
case LONG:
|
||||
return new Long(text);
|
||||
case FLOAT:
|
||||
return new Float(text);
|
||||
case DOUBLE:
|
||||
return new Double(text);
|
||||
case BOOLEAN:
|
||||
return new Boolean(text);
|
||||
case STRING:
|
||||
return text;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String name;
|
||||
public FieldType type;
|
||||
public String valStr;
|
||||
|
||||
/** Deserialization constructor. */
|
||||
public ConfigField ()
|
||||
{
|
||||
}
|
||||
|
||||
/** Construct a new ConfigField with the given values. */
|
||||
public ConfigField (String name, FieldType type, String valStr)
|
||||
{
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.valStr = valStr;
|
||||
}
|
||||
|
||||
// from Comparable<ConfigField>
|
||||
public int compareTo (ConfigField o)
|
||||
{
|
||||
return ComparisonChain.start().compare(name, o.name).result();
|
||||
}
|
||||
|
||||
@Override // from Object
|
||||
public boolean equals (Object o)
|
||||
{
|
||||
return compareTo((ConfigField) o) == 0;
|
||||
}
|
||||
|
||||
@Override // from Object
|
||||
public int hashCode ()
|
||||
{
|
||||
return name.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// $Id: $
|
||||
|
||||
package com.threerings.admin.web.gwt;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gwt.user.client.rpc.IsSerializable;
|
||||
import com.google.gwt.user.client.rpc.RemoteService;
|
||||
|
||||
import com.threerings.web.gwt.ServiceException;
|
||||
|
||||
/**
|
||||
* Defines remote services available to admins.
|
||||
*/
|
||||
public interface ConfigService extends RemoteService
|
||||
{
|
||||
/**
|
||||
* The current runtime configuration of a server, a collection of {@link ConfigurationRecord}
|
||||
* objects indexed by key.
|
||||
*/
|
||||
public static class ConfigurationResult
|
||||
implements IsSerializable
|
||||
{
|
||||
public Map<String, ConfigurationRecord> records;
|
||||
}
|
||||
|
||||
/**
|
||||
* The runtime configuration of a single {@link com.threerings.admin.data.ConfigObject}.
|
||||
*/
|
||||
public static class ConfigurationRecord
|
||||
implements IsSerializable
|
||||
{
|
||||
public ConfigField[] fields;
|
||||
public int updates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all the runtime configuration held by the server and return it in a format
|
||||
* that is digestible by GWT.
|
||||
*/
|
||||
public ConfigurationResult getConfiguration () throws ServiceException;
|
||||
|
||||
/**
|
||||
* Submit a collection of updated fields to the server for application to its runtime
|
||||
* configuration. A new snapshot of the configuration state is returned for sanity checking
|
||||
* purposes.
|
||||
*/
|
||||
public ConfigurationRecord updateConfiguration (String key, ConfigField[] updates)
|
||||
throws ServiceException;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// $Id: $
|
||||
|
||||
package com.threerings.admin.web.gwt;
|
||||
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
|
||||
import com.threerings.admin.web.gwt.ConfigService.ConfigurationRecord;
|
||||
import com.threerings.admin.web.gwt.ConfigService.ConfigurationResult;
|
||||
|
||||
/**
|
||||
* Provides the asynchronous version of {@link com.threerings.admin.web.gwt.AdminService}.
|
||||
*/
|
||||
public interface ConfigServiceAsync
|
||||
{
|
||||
/**
|
||||
* The async version of {@link com.threerings.admin.web.gwt.ConfigService#getConfig}.
|
||||
*/
|
||||
public void getConfiguration (AsyncCallback<ConfigurationResult> callback);
|
||||
|
||||
/**
|
||||
* The async version of {@link com.threerings.admin.web.gwt.ConfigService#updateConfiguration}.
|
||||
*/
|
||||
public void updateConfiguration (
|
||||
String key, ConfigField[] updates, AsyncCallback<ConfigurationRecord> callback);
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
//
|
||||
// $Id: $
|
||||
|
||||
package com.threerings.admin.web.server;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import com.threerings.presents.data.InvocationCodes;
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.dobj.ObjectAccessException;
|
||||
import com.threerings.presents.dobj.RootDObjectManager;
|
||||
|
||||
import com.threerings.web.gwt.ServiceException;
|
||||
import com.threerings.web.server.ServletWaiter;
|
||||
|
||||
import com.threerings.admin.server.ConfigRegistry;
|
||||
import com.threerings.admin.web.gwt.ConfigField;
|
||||
import com.threerings.admin.web.gwt.ConfigField.FieldType;
|
||||
import com.threerings.admin.web.gwt.ConfigService;
|
||||
|
||||
import static com.threerings.admin.Log.log;
|
||||
|
||||
/**
|
||||
* Provides the server implementation of {@link ConfigService}.
|
||||
*/
|
||||
public abstract class ConfigServlet extends RemoteServiceServlet
|
||||
implements ConfigService
|
||||
{
|
||||
// from interface ConfigService
|
||||
public ConfigurationResult getConfiguration ()
|
||||
throws ServiceException
|
||||
{
|
||||
requireAdminUser();
|
||||
|
||||
final ServletWaiter<ConfigurationResult> waiter =
|
||||
new ServletWaiter<ConfigurationResult>("getConfiguration");
|
||||
|
||||
_omgr.postRunnable(new Runnable() {
|
||||
public void run () {
|
||||
Map<String, ConfigurationRecord> tabs = Maps.newHashMap();
|
||||
for (String key : _confReg.getKeys()) {
|
||||
ConfigurationRecord record = buildRecord(key);
|
||||
if (record == null) {
|
||||
waiter.requestFailed(
|
||||
new ServiceException(InvocationCodes.E_INTERNAL_ERROR));
|
||||
return;
|
||||
}
|
||||
tabs.put(key, record);
|
||||
}
|
||||
|
||||
ConfigurationResult result = new ConfigurationResult();
|
||||
result.records = tabs;
|
||||
waiter.requestCompleted(result);
|
||||
}
|
||||
});
|
||||
|
||||
return waiter.waitForResult();
|
||||
}
|
||||
|
||||
// from interface ConfigService
|
||||
public ConfigurationRecord updateConfiguration (final String key, final ConfigField[] updates)
|
||||
throws ServiceException
|
||||
{
|
||||
requireAdminUser();
|
||||
|
||||
final ServletWaiter<ConfigurationRecord> waiter =
|
||||
new ServletWaiter<ConfigurationRecord>("updateConfiguration");
|
||||
|
||||
_omgr.postRunnable(new Runnable() {
|
||||
public void run () {
|
||||
DObject object = _confReg.getObject(key);
|
||||
object.startTransaction();
|
||||
|
||||
int updateCount = 0;
|
||||
for (ConfigField update : updates) {
|
||||
try {
|
||||
object.changeAttribute(update.name, update.type.toValue(update.valStr));
|
||||
updateCount ++;
|
||||
} catch (ObjectAccessException oae) {
|
||||
log.warning("Failed to update field", "field", update.name, oae);
|
||||
}
|
||||
}
|
||||
object.commitTransaction();
|
||||
|
||||
ConfigurationRecord record = buildRecord(key);
|
||||
record.updates = updateCount;
|
||||
waiter.requestCompleted(record);
|
||||
}
|
||||
});
|
||||
|
||||
return waiter.waitForResult();
|
||||
}
|
||||
|
||||
protected ConfigurationRecord buildRecord (String key)
|
||||
{
|
||||
DObject object = _confReg.getObject(key);
|
||||
List<ConfigField> configFields = Lists.newArrayList();
|
||||
Field[] fields = object.getClass().getFields();
|
||||
for (Field field : fields) {
|
||||
if (field.getModifiers() != Modifier.PUBLIC) {
|
||||
continue;
|
||||
}
|
||||
FieldType type = TYPES.get(field.getType());
|
||||
if (type == null) {
|
||||
log.warning("Unknown field type", "field", field.getName(),
|
||||
"type", field.getType());
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Object value = field.get(object);
|
||||
String valStr = type.toString(value);
|
||||
configFields.add(new ConfigField(field.getName(), type, valStr));
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
log.warning("Failure reflecting on configuration object", "key", key,
|
||||
"object", object, "field", field, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
ConfigurationRecord record = new ConfigurationRecord();
|
||||
record.fields = Iterables.toArray(configFields, ConfigField.class);
|
||||
return record;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implemented on a project by project basis to provide a security fence for configuration
|
||||
* editing powers.
|
||||
*/
|
||||
protected abstract void requireAdminUser ()
|
||||
throws ServiceException;
|
||||
|
||||
@Inject protected ConfigRegistry _confReg;
|
||||
@Inject protected RootDObjectManager _omgr;
|
||||
|
||||
protected static Map<Class<?>, FieldType> TYPES = ImmutableMap.<Class<?>, FieldType>builder()
|
||||
.put(Integer.class, FieldType.INTEGER)
|
||||
.put(Integer.TYPE, FieldType.INTEGER)
|
||||
.put(Short.class, FieldType.SHORT)
|
||||
.put(Short.TYPE, FieldType.SHORT)
|
||||
.put(Long.class, FieldType.LONG)
|
||||
.put(Long.TYPE, FieldType.LONG)
|
||||
.put(Float.class, FieldType.FLOAT)
|
||||
.put(Float.TYPE, FieldType.FLOAT)
|
||||
.put(Double.class, FieldType.DOUBLE)
|
||||
.put(Double.TYPE, FieldType.DOUBLE)
|
||||
.put(Boolean.class, FieldType.BOOLEAN)
|
||||
.put(Boolean.TYPE, FieldType.BOOLEAN)
|
||||
.put(String.class, FieldType.STRING)
|
||||
.build();
|
||||
}
|
||||
Reference in New Issue
Block a user