package javax.management.modelmbean;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.io.Serializable;
import java.io.StreamCorruptedException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.management.Descriptor;
import javax.management.MBeanException;
import javax.management.RuntimeOperationsException;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.jboss.mx.modelmbean.ModelMBeanConstants;
import org.jboss.mx.util.Serialization;
import org.jboss.util.xml.JBossEntityResolver;
public class DescriptorSupport
implements Descriptor
{
private Map fieldMap;
private static final int DEFAULT_SIZE = 20;
private static final long serialVersionUID;
private static final ObjectStreamField[] serialPersistentFields;
static
{
switch (Serialization.version)
{
case Serialization.V1R0:
serialVersionUID = 8071560848919417985L;
break;
default:
serialVersionUID = -6292969195866300415L;
}
serialPersistentFields = new ObjectStreamField[]
{
new ObjectStreamField("descriptor", HashMap.class)
};
}
public DescriptorSupport()
{
fieldMap = Collections.synchronizedMap(new HashMap(DEFAULT_SIZE));
}
public DescriptorSupport(int initialSize) throws MBeanException
{
if (initialSize <= 0)
throw new RuntimeOperationsException(new IllegalArgumentException("initialSize <= 0"));
fieldMap = Collections.synchronizedMap(new HashMap(initialSize));
}
public DescriptorSupport(DescriptorSupport descriptor)
{
if (descriptor != null)
{
String[] fieldNames = descriptor.getFieldNames();
fieldMap = Collections.synchronizedMap(new HashMap(fieldNames.length));
this.setFields(fieldNames, descriptor.getFieldValues(fieldNames));
}
else
{
fieldMap = Collections.synchronizedMap(new HashMap(DEFAULT_SIZE));
}
}
public DescriptorSupport(String[] fieldNames, Object[] fieldValues)
throws RuntimeOperationsException
{
fieldMap = Collections.synchronizedMap(new HashMap(DEFAULT_SIZE));
setFields(fieldNames, fieldValues);
}
public DescriptorSupport(String[] fields)
{
if (fields == null)
{
fieldMap = Collections.synchronizedMap(new HashMap(DEFAULT_SIZE));
return;
}
int j = 0;
for (int i = 0; i < fields.length; ++i)
{
if (fields[i] != null && fields[i].length() != 0)
{
++j;
}
}
fieldMap = Collections.synchronizedMap(new HashMap(j));
String[] names = new String[j];
String[] values = new String[j];
j = 0;
for (int i = 0; i < fields.length; ++i)
{
if (fields[i] == null || fields[i].length() == 0)
continue;
try
{
int index = fields[i].indexOf('=');
if (index == -1)
throw new IllegalArgumentException("Invalid field " + fields[i]);
names[j] = fields[i].substring(0, index);
if (index == fields[i].length()-1)
values[j] = null;
else
values[j] = fields[i].substring(index + 1, fields[i].length());
++j;
}
catch (RuntimeException e)
{
throw new RuntimeOperationsException(e, "Error in field " + i);
}
}
setFields(names, values);
}
public DescriptorSupport(String xmlString)
throws MBeanException, RuntimeOperationsException, XMLParseException
{
if (xmlString == null)
throw new RuntimeOperationsException(new IllegalArgumentException("Null xmlString"));
fieldMap = Collections.synchronizedMap(new HashMap(DEFAULT_SIZE));
try
{
SAXReader saxReader = new SAXReader();
saxReader.setEntityResolver(new JBossEntityResolver());
Document document = saxReader.read(new StringReader(xmlString));
Element root = document.getRootElement();
String rootName = root.getName();
if (rootName.equalsIgnoreCase("Descriptor"))
{
for (Iterator i = root.elementIterator(); i.hasNext();)
{
Element element = (Element) i.next();
if (element.getName().equals("field"))
{
Attribute attr = element.attribute("name");
if (attr != null)
{
String name = attr.getText();
String value = element.getTextTrim();
setField(name, value);
}
else
{
throw new XMLParseException("Cannot find attribute 'name' in " + element);
}
}
}
}
else
{
RuntimeException ex = new IllegalArgumentException(
"Root element must be Descriptor, saw: "+rootName);
throw new RuntimeOperationsException(ex);
}
}
catch (DocumentException e)
{
throw new XMLParseException(e, "Cannot parse XML string: " + xmlString);
}
}
public Object getFieldValue(String inFieldName)
{
try
{
checkFieldName(inFieldName);
return fieldMap.get(new FieldName(inFieldName));
}
catch (RuntimeException e)
{
throw new RuntimeOperationsException(e, e.toString());
}
}
public void setField(String inFieldName, Object fieldValue)
{
try
{
checkFieldName(inFieldName);
validateField(inFieldName, fieldValue);
fieldMap.put(new FieldName(inFieldName), fieldValue);
}
catch (RuntimeException e)
{
throw new RuntimeOperationsException(e, e.toString());
}
}
public String[] getFields()
{
String[] fieldStrings = new String[fieldMap.size()];
Iterator it = fieldMap.keySet().iterator();
synchronized (fieldMap)
{
for (int i = 0; i < fieldMap.size(); ++i)
{
FieldName key = (FieldName)it.next();
Object value = fieldMap.get(key);
if (value != null)
{
if (value instanceof String)
fieldStrings[i] = key + "=" + value;
else
fieldStrings[i] = key + "=(" + value + ")";
}
else
{
fieldStrings[i] = key + "=";
}
}
}
return fieldStrings;
}
public String[] getFieldNames()
{
String[] fields = new String[fieldMap.size()];
Iterator it = fieldMap.keySet().iterator();
synchronized (fieldMap)
{
for (int i = 0; i < fieldMap.size(); ++i)
{
FieldName key = (FieldName)it.next();
fields[i] = key.getName();
}
}
return fields;
}
public Object[] getFieldValues(String[] fieldNames)
{
if (fieldMap.size() == 0)
return new Object[0];
Object[] values = null;
if (fieldNames == null)
{
values = new Object[fieldMap.size()];
Iterator it = fieldMap.values().iterator();
synchronized (fieldMap)
{
for (int i = 0; i < fieldMap.size(); ++i)
values[i] = it.next();
}
}
else
{
values = new Object[fieldNames.length];
for (int i = 0; i < fieldNames.length; ++i)
{
if (fieldNames[i] == null || fieldNames[i].equals(""))
values[i] = null;
else
values[i] = fieldMap.get(new FieldName(fieldNames[i]));
}
}
return values;
}
public void setFields(String[] fieldNames, Object[] fieldValues)
{
if (fieldNames == null || fieldValues == null)
throw new RuntimeOperationsException(new IllegalArgumentException("fieldNames or fieldValues was null."));
if (fieldNames.length == 0 && fieldValues.length == 0)
return;
if (fieldNames.length != fieldValues.length)
throw new RuntimeOperationsException(new IllegalArgumentException("fieldNames and fieldValues array size must match."));
try
{
for (int i = 0; i < fieldNames.length; ++i)
{
String name = fieldNames[i];
checkFieldName(name);
validateField(name, fieldValues[i]);
fieldMap.put(new FieldName(name), fieldValues[i]);
}
}
catch (IllegalArgumentException e)
{
throw new RuntimeOperationsException(e);
}
}
public synchronized Object clone()
{
try
{
DescriptorSupport clone = (DescriptorSupport)super.clone();
clone.fieldMap = Collections.synchronizedMap(new HashMap(this.fieldMap));
return clone;
}
catch (CloneNotSupportedException e)
{
throw new RuntimeOperationsException(new RuntimeException(e.getMessage()), e.toString());
}
}
public void removeField(String fieldName)
{
if (fieldName == null || fieldName.equals(""))
return;
fieldMap.remove(new FieldName(fieldName));
}
public boolean isValid()
throws RuntimeOperationsException
{
try
{
validateString(ModelMBeanConstants.NAME, true);
validateString(ModelMBeanConstants.DESCRIPTOR_TYPE, true);
synchronized (fieldMap)
{
for (Iterator i = fieldMap.entrySet().iterator(); i.hasNext(); )
{
Map.Entry entry = (Map.Entry) i.next();
FieldName name = (FieldName) entry.getKey();
Object value = entry.getValue();
validateField(name.getName(), value);
}
}
}
catch (RuntimeException e)
{
return false;
}
return true;
}
public String toXMLString()
throws RuntimeOperationsException
{
if( fieldMap.size() == 0 )
return "<Descriptor></Descriptor>";
try
{
Document document = DocumentHelper.createDocument();
Element root = document.addElement("Descriptor");
String[] names = getFieldNames();
for (int i = 0; i < names.length; i++)
{
String name = names[i];
Object value = getFieldValue(name);
Element field = root.addElement("field");
field.addAttribute("name", name);
field.addText(value.toString());
}
StringWriter sw = new StringWriter();
OutputFormat format = OutputFormat.createPrettyPrint();
XMLWriter writer = new XMLWriter(sw, format);
writer.write(document);
writer.close();
return sw.toString();
}
catch (IOException e)
{
throw new RuntimeOperationsException(new RuntimeException(e),
"Cannot get XML representation");
}
}
public String toString()
{
String[] names = getFieldNames();
Object[] values = getFieldValues(names);
StringBuffer sbuf = new StringBuffer(500);
sbuf.append(getClass()).append('@').append(System.identityHashCode(this)).append('[');
if (names.length == 0)
return "<empty descriptor>";
else
{
for (int i = 0; i < values.length; ++i)
{
sbuf.append(names[i]);
sbuf.append("=");
sbuf.append(values[i]);
if (i < values.length - 1)
sbuf.append(",");
}
}
sbuf.append(']');
return sbuf.toString();
}
private void checkFieldName(String inFieldName)
{
if (inFieldName == null || inFieldName.equals(""))
throw new IllegalArgumentException("null or empty field name");
}
private void validateField(String inFieldName, Object value)
{
String fieldName = inFieldName;
if (fieldName.equalsIgnoreCase(ModelMBeanConstants.NAME))
validateString(inFieldName, value, true);
else if (fieldName.equalsIgnoreCase(ModelMBeanConstants.DESCRIPTOR_TYPE))
validateString(inFieldName, value, true);
else if (fieldName.equalsIgnoreCase(ModelMBeanConstants.CLASS))
validateString(inFieldName, value, false);
else if (fieldName.equalsIgnoreCase(ModelMBeanConstants.ROLE))
validateString(inFieldName, value, false);
else if (fieldName.equalsIgnoreCase(ModelMBeanConstants.GET_METHOD))
validateString(inFieldName, value, false);
else if (fieldName.equalsIgnoreCase(ModelMBeanConstants.SET_METHOD))
validateString(inFieldName, value, false);
else if (fieldName.equalsIgnoreCase(ModelMBeanConstants.PERSIST_PERIOD))
validateNumeric(inFieldName, value);
else if (fieldName.equalsIgnoreCase(ModelMBeanConstants.CURRENCY_TIME_LIMIT))
validateNumeric(inFieldName, value);
else if (fieldName.equalsIgnoreCase(ModelMBeanConstants.LAST_UPDATED_TIME_STAMP))
validateNumeric(inFieldName, value);
else if (fieldName.equalsIgnoreCase(ModelMBeanConstants.LAST_UPDATED_TIME_STAMP2))
validateNumeric(inFieldName, value);
else if (fieldName.equalsIgnoreCase(ModelMBeanConstants.LAST_RETURNED_TIME_STAMP))
validateNumeric(inFieldName, value);
else if (fieldName.equalsIgnoreCase(ModelMBeanConstants.LOG))
validateBoolean(inFieldName, value);
else if (fieldName.equalsIgnoreCase(ModelMBeanConstants.VISIBILITY))
validateNumeric(inFieldName, value, 1, 4);
else if (fieldName.equalsIgnoreCase(ModelMBeanConstants.SEVERITY))
validateNumeric(inFieldName, value, 1, 6);
else if (fieldName.equalsIgnoreCase(ModelMBeanConstants.PERSIST_POLICY))
validatePersistPolicy(inFieldName, value);
}
private void validateString(String fieldName, boolean mandatory)
{
Object value = fieldMap.get(new FieldName(fieldName));
validateString(fieldName, value, mandatory);
}
private void validateString(String fieldName, Object value, boolean mandatory)
{
if (value == null && mandatory == true)
throw new IllegalArgumentException("Expected a value for mandatory field '" + fieldName + "'");
else if (value == null)
throw new IllegalArgumentException("Expected a value for field '" + fieldName + "'");
if ((value instanceof String) == false)
throw new IllegalArgumentException("Expected a String for field '" + fieldName + "'");
String string = (String) value;
if (string.length() == 0)
throw new IllegalArgumentException("Empty value for field '" + fieldName + "'");
}
private void validatePersistPolicy(String fieldName, Object value)
{
validateString(fieldName, value, false);
String string = ((String) value);
String[] policies = ModelMBeanConstants.PERSIST_POLICIES;
for (int i = 0; i < policies.length; ++i)
{
if (policies[i].equalsIgnoreCase(string))
return;
}
throw new IllegalArgumentException("Invalid value " + value + " for field '" + fieldName +
"' expected one of " + Arrays.asList(policies));
}
private void validateBoolean(String fieldName, Object value)
{
if (value == null)
throw new IllegalArgumentException("Expected a value for field '" + fieldName + "'");
if (value instanceof String)
{
String string = ((String) value);
if (string.equalsIgnoreCase("T") || string.equalsIgnoreCase("F"))
return;
if (string.equalsIgnoreCase("TRUE") || string.equalsIgnoreCase("FALSE"))
return;
}
else if (value instanceof Boolean)
return;
throw new IllegalArgumentException("Invalid value " + value + " for field '" + fieldName + "'");
}
private long validateNumeric(String fieldName, Object value)
{
if (value == null)
throw new IllegalArgumentException("Expected a value for field '" + fieldName + "'");
Long number = null;
if (value instanceof String)
number = new Long((String) value);
else if (value instanceof Number)
number = new Long(((Number) value).longValue());
if (number != null && number.longValue() >= -1)
return number.longValue();
throw new IllegalArgumentException("Invalid value " + value + " for field '" + fieldName + "'");
}
private void validateNumeric(String fieldName, Object value, int min, int max)
{
long result = validateNumeric(fieldName, value);
if (result >= min && result <= max)
return;
throw new IllegalArgumentException("Invalid value " + value + " for field '" + fieldName + "'");
}
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException
{
ObjectInputStream.GetField getField = ois.readFields();
HashMap serMap = (HashMap) getField.get("descriptor", null);
if (serMap == null)
throw new StreamCorruptedException("Null descriptor?");
fieldMap = Collections.synchronizedMap(new HashMap());
Iterator it = serMap.entrySet().iterator();
while (it.hasNext())
{
Map.Entry entry = (Map.Entry) it.next();
FieldName key = new FieldName((String)entry.getKey());
fieldMap.put(key, entry.getValue());
}
}
private void writeObject(ObjectOutputStream oos)
throws IOException
{
ObjectOutputStream.PutField putField = oos.putFields();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream tstOOS = new ObjectOutputStream(baos);
HashMap serMap = new HashMap();
Iterator it = fieldMap.entrySet().iterator();
while (it.hasNext())
{
Map.Entry entry = (Map.Entry) it.next();
String key = ((FieldName)entry.getKey()).name;
Object value = entry.getValue();
if( value instanceof Serializable )
{
try
{
baos.reset();
tstOOS.writeObject(value);
serMap.put(key, value);
}
catch(Exception ignore)
{
}
}
}
baos.close();
tstOOS.close();
putField.put("descriptor", serMap);
oos.writeFields();
}
private class FieldName implements Serializable
{
static final long serialVersionUID = 2645619836053638810L;
private String name;
private int hashCode;
public FieldName(String aName)
{
if (aName == null)
throw new IllegalArgumentException("null name");
this.name = aName;
}
public String getName()
{
return name;
}
public int hashCode()
{
if (hashCode == 0)
return hashCode = name.toLowerCase().hashCode();
else
return hashCode;
}
public boolean equals(Object obj)
{
if (obj == null) return false;
if (obj == this) return true;
if (obj instanceof FieldName)
return name.equalsIgnoreCase(((FieldName) obj).name);
if (obj instanceof String)
return name.equalsIgnoreCase((String) obj);
return false;
}
public String toString()
{
return name;
}
}
}