package javax.management;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.io.StreamCorruptedException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import org.jboss.mx.util.ObjectNamePatternHelper;
import org.jboss.mx.util.ObjectNamePatternHelper.PropertyPattern;
import org.jboss.mx.util.QueryExpSupport;
import org.jboss.mx.util.Serialization;
public class ObjectName
implements java.io.Serializable, QueryExp
{
private transient boolean hasPattern = false;
private transient boolean hasDomainPattern = false;
private transient boolean hasPropertyPattern = false;
private transient Hashtable propertiesHash = null;
private transient ArrayList propertiesList = null;
private transient String domain = null;
private transient String kProps = null;
private transient String ckProps = null;
private transient String cName = null;
private transient String constructorProps;
private transient int hash;
private transient PropertyPattern propertyPattern;
private static final long serialVersionUID;
private static final ObjectStreamField[] serialPersistentFields;
static
{
switch (Serialization.version)
{
case Serialization.V1R0:
serialVersionUID = -5467795090068647408L;
serialPersistentFields = new ObjectStreamField[]
{
new ObjectStreamField("domain", String.class),
new ObjectStreamField("propertyList", Hashtable.class),
new ObjectStreamField("propertyListString", String.class),
new ObjectStreamField("canonicalName" , String.class),
new ObjectStreamField("pattern" , Boolean.TYPE),
new ObjectStreamField("propertyPattern" , Boolean.TYPE)
};
break;
default:
serialVersionUID = 1081892073854801359L;
serialPersistentFields = new ObjectStreamField[0];
}
}
public static ObjectName getInstance(String name)
throws MalformedObjectNameException, NullPointerException
{
return new ObjectName(name);
}
public static ObjectName getInstance(String domain, String key, String value)
throws MalformedObjectNameException, NullPointerException
{
return new ObjectName(domain, key, value);
}
public static ObjectName getInstance(String domain, Hashtable table)
throws MalformedObjectNameException, NullPointerException
{
return new ObjectName(domain, table);
}
public static ObjectName getInstance(ObjectName name)
throws NullPointerException
{
return name;
}
public static String quote(String value)
throws NullPointerException
{
if (value == null)
throw new NullPointerException("null value");
StringBuffer buffer = new StringBuffer(value.length() + 10);
buffer.append('"');
char[] chars = value.toCharArray();
for (int i = 0; i < chars.length; i++)
{
switch (chars[i])
{
case '"':
case '*':
case '?':
case '\\':
buffer.append('\\').append(chars[i]);
break;
case '\n':
buffer.append('\\').append('n');
break;
default:
buffer.append(chars[i]);
}
}
buffer.append('"');
return buffer.toString();
}
public static String unquote(String value)
throws IllegalArgumentException, NullPointerException
{
if (value == null)
throw new NullPointerException("null value");
if (value.length() == 0)
throw new IllegalArgumentException("Empty value");
StringBuffer buffer = new StringBuffer(value.length());
char[] chars = value.toCharArray();
boolean inQuote = false;
boolean escape = false;
for (int i = 0; i < chars.length; ++i)
{
char c = chars[i];
if (escape)
{
switch (c)
{
case '\\':
case '"':
case '*':
case '?':
escape = false;
buffer.append(c);
break;
case 'n':
escape = false;
buffer.append('\n');
break;
default:
throw new IllegalArgumentException(
"The value " + value + " contains an invalid escape sequence backslash " + c);
}
}
else if (inQuote == false && c == '"')
inQuote = true;
else if (inQuote && c == '"')
inQuote = false;
else if (inQuote && c == '\\')
escape = true;
else if (inQuote)
{
switch (c)
{
case '"':
throw new IllegalArgumentException(
"The value " + value + " cannot contain quote inside a quote pair, use backslash quote");
case '*':
case '?':
throw new IllegalArgumentException(
"The value " + value + " cannot contain " + c + " inside a quote pair, use backslash " + c);
case '\n':
throw new IllegalArgumentException(
"The value " + value + " cannot contain a new line inside a quote pair, use backslash n");
}
buffer.append(c);
}
else
throw new IllegalArgumentException(
"The value " + value + " is not enclosed in quotes");
}
if (inQuote)
throw new IllegalArgumentException(
"Unterminated quote pair, missing quote");
if (escape)
throw new IllegalArgumentException(
"Unterminated escape, missing one of backslash quote asterisk question mark or n");
return buffer.toString();
}
public ObjectName(String name)
throws MalformedObjectNameException, NullPointerException
{
init(name);
}
public ObjectName(String domain, String key, String value)
throws MalformedObjectNameException, NullPointerException
{
if (null == key)
throw new NullPointerException("properties key cannot be null");
if (null == value)
throw new NullPointerException("properties value cannot be null");
if (key.length() == 0)
throw new MalformedObjectNameException("properties key cannot be empty");
if (value.length() == 0)
throw new MalformedObjectNameException("properties value cannot be empty");
initDomain(domain, null);
Hashtable ptable = new Hashtable();
ptable.put(key, value);
initProperties(ptable, null);
}
public ObjectName(String domain, Hashtable table)
throws MalformedObjectNameException, NullPointerException
{
if (table == null)
throw new NullPointerException("null table");
initDomain(domain, null);
if (table.size() < 1)
throw new MalformedObjectNameException("empty table");
initProperties((Hashtable) table.clone(), null);
}
public boolean equals(Object object)
{
if (object == this)
return true;
if (object == null)
return false;
if (object instanceof ObjectName)
{
ObjectName oname = (ObjectName) object;
return (oname.hash == hash && domain.equals(oname.domain) &&
ckProps.equals(oname.ckProps));
}
return false;
}
public int hashCode()
{
return hash;
}
public String toString()
{
return getCanonicalName();
}
public boolean isPattern()
{
return hasPattern;
}
public String getCanonicalName()
{
if (cName == null)
{
cName = domain + ":" + ckProps;
if (ckProps.length() == 0 && hasPropertyPattern)
{
cName += "*";
}
if (ckProps.length() > 0 && hasPropertyPattern)
{
cName += ",*";
}
}
return cName;
}
public String getDomain()
{
return domain;
}
public String getKeyProperty(String property)
throws NullPointerException
{
return (String) propertiesHash.get(property);
}
public Hashtable getKeyPropertyList()
{
return (Hashtable) propertiesHash.clone();
}
public String getKeyPropertyListString()
{
if (constructorProps != null)
return constructorProps;
else if (kProps == null)
{
StringBuffer buffer = new StringBuffer();
Iterator it = propertiesHash.keySet().iterator();
if (propertiesList != null)
it = propertiesList.iterator();
for (int i=0; it.hasNext(); i++)
{
if (i > 0) buffer.append(",");
String key = (String) it.next();
String val = (String)propertiesHash.get(key);
buffer.append(key + "=" + val);
}
kProps = buffer.toString();
}
return kProps;
}
public String getCanonicalKeyPropertyListString()
{
if (ckProps == null)
{
StringBuffer buffer = new StringBuffer();
ArrayList list = new ArrayList(propertiesHash.keySet());
Collections.sort(list);
Iterator it = list.iterator();
for (int i=0; it.hasNext(); i++)
{
if (i > 0) buffer.append(",");
String key = (String) it.next();
String val = (String)propertiesHash.get(key);
buffer.append(key + "=" + val);
}
ckProps = buffer.toString();
}
return ckProps;
}
public boolean isPropertyPattern()
{
return hasPropertyPattern;
}
public boolean isDomainPattern()
{
return hasDomainPattern;
}
public boolean apply(ObjectName name)
throws NullPointerException
{
if (name.isPattern())
return false;
if (this == name)
return true;
if (ObjectNamePatternHelper.patternMatch(name.getDomain(), this.getDomain()))
{
if (propertyPattern == null)
propertyPattern = new PropertyPattern(this);
return propertyPattern.patternMatch(name);
}
return false;
}
public void setMBeanServer(MBeanServer server)
{
QueryExpSupport.server.set(server);
}
private void init(String name) throws MalformedObjectNameException
{
if (name == null)
throw new NullPointerException("null name");
if (name.length() == 0)
name = "*:*";
int domainSep = name.indexOf(':');
if (-1 == domainSep)
throw new MalformedObjectNameException("missing domain");
initDomain(name.substring(0, domainSep), name);
constructorProps = name.substring(domainSep + 1);
initProperties(constructorProps, name);
if (constructorProps.equals("*"))
constructorProps = "";
else if (constructorProps.startsWith("*,"))
constructorProps = constructorProps.substring(2);
else if (constructorProps.endsWith(",*"))
constructorProps = constructorProps.substring(0, constructorProps.length()-2);
}
private void initDomain(String dstring, String name) throws MalformedObjectNameException
{
if (null == dstring)
throw new NullPointerException("null domain");
checkIllegalDomain(dstring, name);
if (dstring.indexOf('*') > -1 || dstring.indexOf('?') > -1)
{
this.hasPattern = true;
this.hasDomainPattern = true;
}
this.domain = dstring;
}
private void initProperties(String properties, String name) throws MalformedObjectNameException
{
if (null == properties || properties.length() < 1)
throw new MalformedObjectNameException(addDebugObjectName(name) +
"null or empty properties");
Hashtable ptable = new Hashtable();
this.propertiesList = new ArrayList();
char[] chars = properties.toCharArray();
String key = null;
int start = 0;
boolean inKey = true;
boolean inQuote = false;
boolean escape = false;
boolean endOrTerminate = false;
for (int current = 0; current < chars.length; ++current)
{
char c = chars[current];
if (escape)
escape = false;
else if (inQuote)
{
if (c == '"')
{
inQuote = false;
endOrTerminate = true;
}
else if (c == '\\')
escape = true;
}
else if (inKey)
{
if (c == '=' || c == ',')
{
if (current == start)
throw new MalformedObjectNameException(addDebugObjectName(name) +
"Empty key");
key = properties.substring(start, current);
if (c == ',')
{
if (key.equals("*"))
{
if (hasPropertyPattern)
throw new MalformedObjectNameException(addDebugObjectName(name) +
"A property pattern may only contain one * property");
this.hasPropertyPattern = true;
this.hasPattern = true;
}
else
throw new MalformedObjectNameException(addDebugObjectName(name) +
"Invalid key/value data " + key);
}
else
{
if (ptable.containsKey(key))
throw new MalformedObjectNameException(addDebugObjectName(name) +
"Duplicate key " + key);
inKey = false;
}
start = current + 1;
}
}
else
{
if (c == '"')
inQuote = true;
else if (c == ',')
{
if (current == start)
throw new MalformedObjectNameException(addDebugObjectName(name) +
"Invalid key/value data " + key);
else
{
propertiesList.add(key);
ptable.put(key, properties.substring(start, current));
}
inKey = true;
endOrTerminate = false;
start = current + 1;
}
else if (endOrTerminate)
{
throw new MalformedObjectNameException(addDebugObjectName(name) +
"Invalid key/value data " + key);
}
}
}
if (inKey)
{
if (start == chars.length)
throw new MalformedObjectNameException(addDebugObjectName(name) +
"An ObjectName cannot end with a comma");
key = properties.substring(start, chars.length);
if (key.equals("*"))
{
if (hasPropertyPattern)
throw new MalformedObjectNameException(addDebugObjectName(name) +
"A property pattern may only contain one * property");
this.hasPropertyPattern = true;
this.hasPattern = true;
}
else
throw new MalformedObjectNameException(addDebugObjectName(name) +
"Invalid key/value data " + key);
}
if (inKey == false)
{
if (start == chars.length)
throw new MalformedObjectNameException(addDebugObjectName(name) +
"Invalid key/value data " + key);
else
{
propertiesList.add(key);
ptable.put(key, properties.substring(start, chars.length));
}
}
initProperties(ptable, name);
}
private void initProperties(Hashtable properties, String name) throws MalformedObjectNameException
{
if (null == properties || (!this.hasPropertyPattern && properties.size() < 1))
throw new MalformedObjectNameException(addDebugObjectName(name) +
"null or empty properties");
Iterator it = properties.keySet().iterator();
ArrayList list = new ArrayList();
while (it.hasNext())
{
String key = null;
try
{
key = (String) it.next();
}
catch (ClassCastException e)
{
throw new MalformedObjectNameException(addDebugObjectName(name) +
"key is not a string " + key);
}
String val = null;
try
{
val = (String) properties.get(key);
}
catch (ClassCastException e)
{
throw new MalformedObjectNameException(addDebugObjectName(name) +
"value is not a string " + val);
}
if (key.equals("*") && val.equals("*"))
{
it.remove();
this.hasPropertyPattern = true;
this.hasPattern = true;
continue;
}
if (key.length() == 0)
throw new MalformedObjectNameException(addDebugObjectName(name) +
"key has no length =" + val);
if (val.length() == 0)
throw new MalformedObjectNameException(addDebugObjectName(name) +
"value has no length =" + val);
checkIllegalKey(key, name);
checkIllegalValue(val, name);
list.add(new String(key + "=" + val));
}
Collections.sort(list);
StringBuffer strBuffer = new StringBuffer();
it = list.iterator();
while (it.hasNext())
{
strBuffer.append(it.next());
if (it.hasNext())
{
strBuffer.append(',');
}
}
if (this.hasPropertyPattern)
{
if (properties.size() > 0)
{
strBuffer.append(",*");
}
else
{
strBuffer.append("*");
}
}
this.propertiesHash = properties;
this.kProps = getKeyPropertyListString();
this.ckProps = getCanonicalKeyPropertyListString();
this.hash = getCanonicalName().hashCode();
}
private void checkIllegalKey(String key, String name)
throws MalformedObjectNameException
{
char[] chars = key.toCharArray();
for (int i = 0; i < chars.length; ++i)
{
switch (chars[i])
{
case ':':
case ',':
case '=':
case '*':
case '?':
throw new MalformedObjectNameException(addDebugObjectName(name) +
"The key " + key + " cannot contain a " + chars[i] + " character");
case '\n':
throw new MalformedObjectNameException(addDebugObjectName(name) +
"The key " + key + " cannot contain a new line character");
}
}
}
private void checkIllegalValue(String value, String name)
throws MalformedObjectNameException
{
char[] chars = value.toCharArray();
boolean inQuote = false;
boolean escape = false;
for (int i = 0; i < chars.length; ++i)
{
char c = chars[i];
if (escape)
{
switch (c)
{
case '\\':
case 'n':
case '"':
case '*':
case '?':
escape = false;
break;
default:
throw new MalformedObjectNameException(addDebugObjectName(name) +
"The value " + value + " contains an invalid escape sequence backslash " + c);
}
}
else if (inQuote == false && c == '"')
inQuote = true;
else if (inQuote && c == '"')
inQuote = false;
else if (inQuote && c == '\\')
escape = true;
else if (inQuote)
{
switch (c)
{
case '"':
throw new MalformedObjectNameException(addDebugObjectName(name) +
"The value " + value + " cannot contain quote inside a quote pair, use backslash quote");
case '*':
case '?':
throw new MalformedObjectNameException(addDebugObjectName(name) +
"The value " + value + " cannot contain " + c + " inside a quote pair, use backslash " + c);
case '\n':
throw new MalformedObjectNameException(addDebugObjectName(name) +
"The value " + value + " cannot contain a new line inside a quote pair, use backslash n");
}
}
else
{
switch (c)
{
case ':':
case ',':
case '=':
case '*':
case '?':
throw new MalformedObjectNameException(addDebugObjectName(name) +
"The value " + value + " cannot contain " + c + " use quote backslash " + c + " quote or ObjectName.quote(String)");
case '\n':
throw new MalformedObjectNameException(addDebugObjectName(name) +
"The value " + value + " cannot contain a new line use quote backslash n quote or ObjectName.quote(String)");
}
}
}
if (inQuote)
throw new MalformedObjectNameException(addDebugObjectName(name) +
"Unterminated quote pair, missing quote");
if (escape)
throw new MalformedObjectNameException(addDebugObjectName(name) +
"Unterminated escape, missing one of backslash quote asterisk question mark or n");
}
private void checkIllegalDomain(String dom, String name)
throws MalformedObjectNameException
{
char[] chars = dom.toCharArray();
for (int i = 0; i < chars.length; i++)
{
switch (chars[i])
{
case ':':
throw new MalformedObjectNameException(addDebugObjectName(name) +
"The domain " + dom + " cannot contain a : character");
case '\n':
throw new MalformedObjectNameException(addDebugObjectName(name) +
"The domain " + dom + " cannot contain a new line character");
}
}
}
private String addDebugObjectName(String name)
{
if (name == null)
return "";
else
return name + " is not a valid ObjectName. ";
}
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException
{
switch (Serialization.version)
{
case Serialization.V1R0:
ObjectInputStream.GetField getField = ois.readFields();
try
{
String name = (String) getField.get("canonicalName", null);
if (name == null)
throw new StreamCorruptedException("No canonical name for jmx1.0?");
init(name);
}
catch (MalformedObjectNameException e)
{
throw new StreamCorruptedException(e.toString());
}
break;
default:
try
{
init((String) ois.readObject());
}
catch (MalformedObjectNameException e)
{
throw new StreamCorruptedException(e.toString());
}
}
}
private void writeObject(ObjectOutputStream oos)
throws IOException
{
switch (Serialization.version)
{
case Serialization.V1R0:
ObjectOutputStream.PutField putField = oos.putFields();
putField.put("domain", domain);
putField.put("propertyList", propertiesHash);
putField.put("propertyListString", ckProps);
putField.put("canonicalName", domain + ":" + ckProps);
putField.put("pattern", hasPattern);
putField.put("propertyPattern", hasPropertyPattern);
oos.writeFields();
break;
default:
String props = getKeyPropertyListString();
StringBuffer buffer = new StringBuffer(domain.length() + props.length() + 3);
buffer.append(domain);
buffer.append(':');
buffer.append(props);
if (hasPropertyPattern)
{
if (propertiesHash.size() > 0)
buffer.append(',');
buffer.append('*');
}
oos.writeObject(buffer.toString());
}
}
}