|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package com.sun.jmx.mbeanserver; |
|
|
|
import com.sun.jmx.defaults.ServiceName; |
|
import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER; |
|
|
|
import java.util.ArrayList; |
|
import java.util.Collections; |
|
import java.util.HashMap; |
|
import java.util.HashSet; |
|
import java.util.List; |
|
import java.util.concurrent.locks.ReentrantReadWriteLock; |
|
import java.util.logging.Level; |
|
import java.util.Map; |
|
import java.util.Set; |
|
import javax.management.DynamicMBean; |
|
import javax.management.InstanceAlreadyExistsException; |
|
import javax.management.InstanceNotFoundException; |
|
import javax.management.ObjectName; |
|
import javax.management.QueryExp; |
|
import javax.management.RuntimeOperationsException; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class Repository { |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public interface RegistrationContext { |
|
|
|
|
|
|
|
|
|
*/ |
|
public void registering(); |
|
|
|
|
|
|
|
|
|
*/ |
|
public void unregistered(); |
|
} |
|
|
|
// Private fields --------------------------------------------> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private final Map<String,Map<String,NamedObject>> domainTb; |
|
|
|
|
|
|
|
*/ |
|
private volatile int nbElements = 0; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private final String domain; |
|
|
|
|
|
|
|
|
|
|
|
|
|
**/ |
|
private final ReentrantReadWriteLock lock; |
|
|
|
// Private fields <============================================= |
|
|
|
// Private methods ---------------------------------------------> |
|
|
|
|
|
private final static class ObjectNamePattern { |
|
private final String[] keys; |
|
private final String[] values; |
|
private final String properties; |
|
private final boolean isPropertyListPattern; |
|
private final boolean isPropertyValuePattern; |
|
|
|
|
|
|
|
**/ |
|
public final ObjectName pattern; |
|
|
|
|
|
|
|
|
|
**/ |
|
public ObjectNamePattern(ObjectName pattern) { |
|
this(pattern.isPropertyListPattern(), |
|
pattern.isPropertyValuePattern(), |
|
pattern.getCanonicalKeyPropertyListString(), |
|
pattern.getKeyPropertyList(), |
|
pattern); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**/ |
|
ObjectNamePattern(boolean propertyListPattern, |
|
boolean propertyValuePattern, |
|
String canonicalProps, |
|
Map<String,String> keyPropertyList, |
|
ObjectName pattern) { |
|
this.isPropertyListPattern = propertyListPattern; |
|
this.isPropertyValuePattern = propertyValuePattern; |
|
this.properties = canonicalProps; |
|
final int len = keyPropertyList.size(); |
|
this.keys = new String[len]; |
|
this.values = new String[len]; |
|
int i = 0; |
|
for (Map.Entry<String,String> entry : keyPropertyList.entrySet()) { |
|
keys[i] = entry.getKey(); |
|
values[i] = entry.getValue(); |
|
i++; |
|
} |
|
this.pattern = pattern; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**/ |
|
public boolean matchKeys(ObjectName name) { |
|
// If key property value pattern but not key property list |
|
// pattern, then the number of key properties must be equal |
|
|
|
if (isPropertyValuePattern && |
|
!isPropertyListPattern && |
|
(name.getKeyPropertyList().size() != keys.length)) |
|
return false; |
|
|
|
// If key property value pattern or key property list pattern, |
|
// then every property inside pattern should exist in name |
|
|
|
if (isPropertyValuePattern || isPropertyListPattern) { |
|
for (int i = keys.length - 1; i >= 0 ; i--) { |
|
// Find value in given object name for key at current |
|
// index in receiver |
|
|
|
String v = name.getKeyProperty(keys[i]); |
|
// Did we find a value for this key ? |
|
|
|
if (v == null) return false; |
|
// If this property is ok (same key, same value), go to next |
|
|
|
if (isPropertyValuePattern && |
|
pattern.isPropertyValuePattern(keys[i])) { |
|
// wildmatch key property values |
|
// values[i] is the pattern; |
|
|
|
if (Util.wildmatch(v,values[i])) |
|
continue; |
|
else |
|
return false; |
|
} |
|
if (v.equals(values[i])) continue; |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
// If no pattern, then canonical names must be equal |
|
|
|
final String p1 = name.getCanonicalKeyPropertyListString(); |
|
final String p2 = properties; |
|
return (p1.equals(p2)); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
**/ |
|
private void addAllMatching(final Map<String,NamedObject> moiTb, |
|
final Set<NamedObject> result, |
|
final ObjectNamePattern pattern) { |
|
synchronized (moiTb) { |
|
for (NamedObject no : moiTb.values()) { |
|
final ObjectName on = no.getName(); |
|
|
|
if (pattern.matchKeys(on)) result.add(no); |
|
} |
|
} |
|
} |
|
|
|
private void addNewDomMoi(final DynamicMBean object, |
|
final String dom, |
|
final ObjectName name, |
|
final RegistrationContext context) { |
|
final Map<String,NamedObject> moiTb = |
|
new HashMap<String,NamedObject>(); |
|
final String key = name.getCanonicalKeyPropertyListString(); |
|
addMoiToTb(object,name,key,moiTb,context); |
|
domainTb.put(dom, moiTb); |
|
nbElements++; |
|
} |
|
|
|
private void registering(RegistrationContext context) { |
|
if (context == null) return; |
|
try { |
|
context.registering(); |
|
} catch (RuntimeOperationsException x) { |
|
throw x; |
|
} catch (RuntimeException x) { |
|
throw new RuntimeOperationsException(x); |
|
} |
|
} |
|
|
|
private void unregistering(RegistrationContext context, ObjectName name) { |
|
if (context == null) return; |
|
try { |
|
context.unregistered(); |
|
} catch (Exception x) { |
|
|
|
MBEANSERVER_LOGGER.log(Level.FINE, |
|
"Unexpected exception while unregistering "+name, |
|
x); |
|
} |
|
} |
|
|
|
private void addMoiToTb(final DynamicMBean object, |
|
final ObjectName name, |
|
final String key, |
|
final Map<String,NamedObject> moiTb, |
|
final RegistrationContext context) { |
|
registering(context); |
|
moiTb.put(key,new NamedObject(name, object)); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private NamedObject retrieveNamedObject(ObjectName name) { |
|
|
|
|
|
if (name.isPattern()) return null; |
|
|
|
|
|
String dom = name.getDomain().intern(); |
|
|
|
|
|
if (dom.length() == 0) { |
|
dom = domain; |
|
} |
|
|
|
Map<String,NamedObject> moiTb = domainTb.get(dom); |
|
if (moiTb == null) { |
|
return null; |
|
} |
|
|
|
return moiTb.get(name.getCanonicalKeyPropertyListString()); |
|
} |
|
|
|
// Private methods <============================================= |
|
|
|
// Protected methods ---------------------------------------------> |
|
|
|
// Protected methods <============================================= |
|
|
|
// Public methods ---------------------------------------------> |
|
|
|
|
|
|
|
*/ |
|
public Repository(String domain) { |
|
this(domain,true); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Repository(String domain, boolean fairLock) { |
|
lock = new ReentrantReadWriteLock(fairLock); |
|
|
|
domainTb = new HashMap<String,Map<String,NamedObject>>(5); |
|
|
|
if (domain != null && domain.length() != 0) |
|
this.domain = domain.intern(); |
|
else |
|
this.domain = ServiceName.DOMAIN; |
|
|
|
|
|
domainTb.put(this.domain, new HashMap<String,NamedObject>()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String[] getDomains() { |
|
|
|
lock.readLock().lock(); |
|
final List<String> result; |
|
try { |
|
|
|
result = new ArrayList<String>(domainTb.size()); |
|
for (Map.Entry<String,Map<String,NamedObject>> entry : |
|
domainTb.entrySet()) { |
|
// Skip domains that are in the table but have no |
|
// MBean registered in them |
|
|
|
Map<String,NamedObject> t = entry.getValue(); |
|
if (t != null && t.size() != 0) |
|
result.add(entry.getKey()); |
|
} |
|
} finally { |
|
lock.readLock().unlock(); |
|
} |
|
|
|
|
|
return result.toArray(new String[result.size()]); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void addMBean(final DynamicMBean object, ObjectName name, |
|
final RegistrationContext context) |
|
throws InstanceAlreadyExistsException { |
|
|
|
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { |
|
MBEANSERVER_LOGGER.logp(Level.FINER, Repository.class.getName(), |
|
"addMBean", "name = " + name); |
|
} |
|
|
|
|
|
String dom = name.getDomain().intern(); |
|
boolean to_default_domain = false; |
|
|
|
|
|
if (dom.length() == 0) |
|
name = Util.newObjectName(domain + name.toString()); |
|
|
|
|
|
if (dom == domain) { |
|
to_default_domain = true; |
|
dom = domain; |
|
} else { |
|
to_default_domain = false; |
|
} |
|
|
|
|
|
if (name.isPattern()) { |
|
throw new RuntimeOperationsException( |
|
new IllegalArgumentException("Repository: cannot add mbean for " + |
|
"pattern name " + name.toString())); |
|
} |
|
|
|
lock.writeLock().lock(); |
|
try { |
|
|
|
if ( !to_default_domain && |
|
dom.equals("JMImplementation") && |
|
domainTb.containsKey("JMImplementation")) { |
|
throw new RuntimeOperationsException( |
|
new IllegalArgumentException( |
|
"Repository: domain name cannot be JMImplementation")); |
|
} |
|
|
|
|
|
final Map<String,NamedObject> moiTb = domainTb.get(dom); |
|
if (moiTb == null) { |
|
addNewDomMoi(object, dom, name, context); |
|
return; |
|
} else { |
|
|
|
String cstr = name.getCanonicalKeyPropertyListString(); |
|
NamedObject elmt= moiTb.get(cstr); |
|
if (elmt != null) { |
|
throw new InstanceAlreadyExistsException(name.toString()); |
|
} else { |
|
nbElements++; |
|
addMoiToTb(object,name,cstr,moiTb,context); |
|
} |
|
} |
|
|
|
} finally { |
|
lock.writeLock().unlock(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean contains(ObjectName name) { |
|
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { |
|
MBEANSERVER_LOGGER.logp(Level.FINER, Repository.class.getName(), |
|
"contains", " name = " + name); |
|
} |
|
lock.readLock().lock(); |
|
try { |
|
return (retrieveNamedObject(name) != null); |
|
} finally { |
|
lock.readLock().unlock(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public DynamicMBean retrieve(ObjectName name) { |
|
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { |
|
MBEANSERVER_LOGGER.logp(Level.FINER, Repository.class.getName(), |
|
"retrieve", "name = " + name); |
|
} |
|
|
|
|
|
lock.readLock().lock(); |
|
try { |
|
NamedObject no = retrieveNamedObject(name); |
|
if (no == null) return null; |
|
else return no.getObject(); |
|
} finally { |
|
lock.readLock().unlock(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Set<NamedObject> query(ObjectName pattern, QueryExp query) { |
|
|
|
final Set<NamedObject> result = new HashSet<NamedObject>(); |
|
|
|
// The following filter cases are considered: |
|
// null, "", "*:*" : names in all domains |
|
// ":*", ":[key=value],*" : names in defaultDomain |
|
// "domain:*", "domain:[key=value],*" : names in the specified domain |
|
|
|
|
|
ObjectName name; |
|
if (pattern == null || |
|
pattern.getCanonicalName().length() == 0 || |
|
pattern.equals(ObjectName.WILDCARD)) |
|
name = ObjectName.WILDCARD; |
|
else name = pattern; |
|
|
|
lock.readLock().lock(); |
|
try { |
|
|
|
|
|
if (!name.isPattern()) { |
|
final NamedObject no = retrieveNamedObject(name); |
|
if (no != null) result.add(no); |
|
return result; |
|
} |
|
|
|
|
|
if (name == ObjectName.WILDCARD) { |
|
for (Map<String,NamedObject> moiTb : domainTb.values()) { |
|
result.addAll(moiTb.values()); |
|
} |
|
return result; |
|
} |
|
|
|
final String canonical_key_property_list_string = |
|
name.getCanonicalKeyPropertyListString(); |
|
final boolean allNames = |
|
(canonical_key_property_list_string.length()==0); |
|
final ObjectNamePattern namePattern = |
|
(allNames?null:new ObjectNamePattern(name)); |
|
|
|
|
|
if (name.getDomain().length() == 0) { |
|
final Map<String,NamedObject> moiTb = domainTb.get(domain); |
|
if (allNames) |
|
result.addAll(moiTb.values()); |
|
else |
|
addAllMatching(moiTb, result, namePattern); |
|
return result; |
|
} |
|
|
|
if (!name.isDomainPattern()) { |
|
final Map<String,NamedObject> moiTb = domainTb.get(name.getDomain()); |
|
if (moiTb == null) return Collections.emptySet(); |
|
if (allNames) |
|
result.addAll(moiTb.values()); |
|
else |
|
addAllMatching(moiTb, result, namePattern); |
|
return result; |
|
} |
|
|
|
|
|
final String dom2Match = name.getDomain(); |
|
for (String dom : domainTb.keySet()) { |
|
if (Util.wildmatch(dom, dom2Match)) { |
|
final Map<String,NamedObject> moiTb = domainTb.get(dom); |
|
if (allNames) |
|
result.addAll(moiTb.values()); |
|
else |
|
addAllMatching(moiTb, result, namePattern); |
|
} |
|
} |
|
return result; |
|
} finally { |
|
lock.readLock().unlock(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void remove(final ObjectName name, |
|
final RegistrationContext context) |
|
throws InstanceNotFoundException { |
|
|
|
|
|
if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { |
|
MBEANSERVER_LOGGER.logp(Level.FINER, Repository.class.getName(), |
|
"remove", "name = " + name); |
|
} |
|
|
|
|
|
String dom= name.getDomain().intern(); |
|
|
|
|
|
if (dom.length() == 0) dom = domain; |
|
|
|
lock.writeLock().lock(); |
|
try { |
|
|
|
final Map<String,NamedObject> moiTb = domainTb.get(dom); |
|
if (moiTb == null) { |
|
throw new InstanceNotFoundException(name.toString()); |
|
} |
|
|
|
|
|
if (moiTb.remove(name.getCanonicalKeyPropertyListString())==null) { |
|
throw new InstanceNotFoundException(name.toString()); |
|
} |
|
|
|
|
|
nbElements--; |
|
|
|
|
|
if (moiTb.isEmpty()) { |
|
domainTb.remove(dom); |
|
|
|
// set a new default domain table (always present) |
|
// need to reinstantiate a hashtable because of possible |
|
// big buckets array size inside table, never cleared, |
|
|
|
if (dom == domain) |
|
domainTb.put(domain, new HashMap<String,NamedObject>()); |
|
} |
|
|
|
unregistering(context,name); |
|
|
|
} finally { |
|
lock.writeLock().unlock(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Integer getCount() { |
|
return nbElements; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getDefaultDomain() { |
|
return domain; |
|
} |
|
|
|
// Public methods <============================================= |
|
} |