1. /*
  2. * @(#)RepositorySupport.java 1.65 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.jmx.mbeanserver;
  8. // java import
  9. import java.util.Hashtable;
  10. import java.util.Enumeration;
  11. import java.util.Set;
  12. import java.util.ArrayList;
  13. import java.util.HashSet;
  14. import java.util.Vector;
  15. // RI import
  16. import javax.management.* ;
  17. import com.sun.jmx.defaults.ServiceName;
  18. import com.sun.jmx.trace.Trace;
  19. /**
  20. * The RepositorySupport implements the Repository interface.
  21. * This repository does not support persistency.
  22. *
  23. * @since 1.5
  24. */
  25. public class RepositorySupport implements Repository {
  26. // Private fields -------------------------------------------->
  27. /**
  28. * An object name for query describing the whole set of mbeans.
  29. * Optimization helper for queries.
  30. */
  31. private final static ObjectName _WholeWordQueryObjectName;
  32. static {
  33. try {
  34. _WholeWordQueryObjectName = new ObjectName("*:*");
  35. } catch (MalformedObjectNameException e) {
  36. throw new UnsupportedOperationException(e.getMessage());
  37. }
  38. }
  39. /**
  40. * two int utilities to minimize wildmatch method stack frame overhead
  41. * during recursions.
  42. */
  43. private static int _slen;
  44. private static int _plen;
  45. /**
  46. * The structure for storing the objects is very basic .
  47. * A Hashtable is used for storing the different domains
  48. * For each domain, a hashtable contains the instances with
  49. * canonical key property list string as key and named object
  50. * aggregated from given object name and mbean instance as value.
  51. */
  52. private final Hashtable domainTb;
  53. /**
  54. * Number of elements contained in the Repository
  55. */
  56. private int nbElements = 0;
  57. /**
  58. * Domain name of the server the repository is attached to.
  59. * It is quicker to store the information in the repository rather
  60. * than querying the framework each time the info is required.
  61. */
  62. private final String domain;
  63. /** The name of this class to be used for tracing */
  64. private final static String dbgTag = "Repository";
  65. // Private fields <=============================================
  66. // Private methods --------------------------------------------->
  67. // TRACES & DEBUG
  68. //---------------
  69. private final static boolean isTraceOn() {
  70. return Trace.isSelected(Trace.LEVEL_TRACE, Trace.INFO_MBEANSERVER);
  71. }
  72. private final static void trace(String clz, String func, String info) {
  73. Trace.send(Trace.LEVEL_TRACE, Trace.INFO_MBEANSERVER, clz, func,
  74. info);
  75. }
  76. private final static void trace(String func, String info) {
  77. trace(dbgTag, func, info);
  78. }
  79. private final static boolean isDebugOn() {
  80. return Trace.isSelected(Trace.LEVEL_DEBUG, Trace.INFO_MBEANSERVER);
  81. }
  82. private final static void debug(String clz, String func, String info) {
  83. Trace.send(Trace.LEVEL_DEBUG, Trace.INFO_MBEANSERVER, clz, func,
  84. info);
  85. }
  86. private final static void debug(String func, String info) {
  87. debug(dbgTag, func, info);
  88. }
  89. /* This class is used to match an ObjectName against a pattern. */
  90. private final static class ObjectNamePattern {
  91. private final char[] domain;
  92. private final String[] keys;
  93. private final String[] values;
  94. private final String properties;
  95. private final boolean isPropertyPattern;
  96. /**
  97. * The ObjectName pattern against which ObjectNames are matched.
  98. **/
  99. public final ObjectName pattern;
  100. /**
  101. * Builds a new ObjectNamePattern object from an ObjectName pattern.
  102. * @param pattern The ObjectName pattern under examination.
  103. **/
  104. public ObjectNamePattern(ObjectName pattern) {
  105. this(pattern.isPattern(),pattern.getDomain(),
  106. pattern.isPropertyPattern(),
  107. pattern.getCanonicalKeyPropertyListString(),
  108. pattern.getKeyPropertyList(),pattern);
  109. }
  110. /**
  111. * Builds a new ObjectNamePattern object from an ObjectName pattern
  112. * constituents.
  113. * @param domainPattern pattern.isPattern().
  114. * @param domain pattern.getDomain().
  115. * @param propertyPattern pattern.isPropertyPattern().
  116. * @param canonicalProps pattern.getCanonicalKeyPropertyListString()
  117. * @param keyPropertyList pattern.getKeyPropertyList()
  118. * @param pattern The ObjectName pattern under examination.
  119. **/
  120. ObjectNamePattern(boolean domainPattern, String domain,
  121. boolean propertyPattern, String canonicalProps,
  122. Hashtable keyPropertyList, ObjectName pattern) {
  123. final int len = (keyPropertyList==null?0:keyPropertyList.size());
  124. final Enumeration e =
  125. (keyPropertyList==null?null:keyPropertyList.keys());
  126. this.domain = (domain == null?null:domain.toCharArray());
  127. this.keys = new String[len];
  128. this.values = new String[len];
  129. for (int i = 0 ; i < len ; i++ ) {
  130. final String k = (String)e.nextElement();
  131. keys[i] = k;
  132. values[i] = (String)keyPropertyList.get(k);
  133. }
  134. this.properties = canonicalProps;
  135. this.isPropertyPattern = propertyPattern;
  136. this.pattern = pattern;
  137. }
  138. /**
  139. * Return true if the given ObjectName matches the ObjectName pattern
  140. * for which this object has been built.
  141. * WARNING: domain name is not considered here because it is supposed
  142. * not to be wildcard when called. PropertyList is also
  143. * supposed not to be zero-length.
  144. * @param name The ObjectName we want to match against the pattern.
  145. * @return true if <code>name</code> matches the pattern.
  146. **/
  147. public boolean matchKeys(ObjectName name) {
  148. if (isPropertyPattern) {
  149. // Every property inside pattern should exist in name
  150. for (int i= keys.length -1; i >= 0 ; i--) {
  151. // find value in given object name for key at current
  152. // index in receiver
  153. String v = name.getKeyProperty(keys[i]);
  154. // did we find a value for this key ?
  155. if (v == null) return false;
  156. // if this property is ok (same key, same value),
  157. // go to next
  158. if (v.equals(values[i])) continue;
  159. return false;
  160. }
  161. return true;
  162. } else {
  163. if (keys.length != name.getKeyPropertyList().size())
  164. return false;
  165. final String p1 = name.getCanonicalKeyPropertyListString();
  166. final String p2 = properties;
  167. // if (p1 == null) return (p2 == null);
  168. // if (p2 == null) return p1.equals("");
  169. return (p1.equals(p2));
  170. }
  171. }
  172. }
  173. /**
  174. * Add all the matching objects from the given hashtable in the
  175. * result set for the given ObjectNamePattern
  176. * Do not check whether the domains match (only check for matching
  177. * key property lists - see <i>matchKeys()</i>)
  178. **/
  179. private final void addAllMatching(final Hashtable moiTb, final Set result,
  180. final ObjectNamePattern pattern) {
  181. synchronized (moiTb) {
  182. for (Enumeration e = moiTb.elements(); e.hasMoreElements();) {
  183. final NamedObject no = (NamedObject) e.nextElement();
  184. final ObjectName on = no.getName();
  185. // if all couples (property, value) are contained
  186. if (pattern.matchKeys(on)) result.add(no);
  187. }
  188. }
  189. }
  190. private final void addNewDomMoi(final Object object, final String dom,
  191. final ObjectName name) {
  192. final Hashtable moiTb= new Hashtable();
  193. domainTb.put(dom, moiTb);
  194. moiTb.put(name.getCanonicalKeyPropertyListString(),
  195. new NamedObject(name, object));
  196. nbElements++;
  197. }
  198. /*
  199. * Tests whether string s is matched by pattern p.
  200. * Supports "?", "*" each of which may be escaped with "\";
  201. * Not yet supported: internationalization; "\" inside brackets.<P>
  202. * Wildcard matching routine by Karl Heuer. Public Domain.<P>
  203. */
  204. private static boolean wildmatch(char[] s, char[] p, int si, int pi) {
  205. char c;
  206. // Be careful: this is dangerous: it works because wildmatch
  207. // is protected by a synchronized block on domainTb
  208. _slen = s.length;
  209. _plen = p.length;
  210. // end of comment.
  211. while (pi < _plen) { // While still string
  212. c = p[pi++];
  213. if (c == '?') {
  214. if (++si > _slen) return false;
  215. } else if (c == '*') { // Wildcard
  216. if (pi >= _plen) return true;
  217. do {
  218. if (wildmatch(s,p,si,pi)) return true;
  219. } while (++si < _slen);
  220. return false;
  221. } else {
  222. if (si >= _slen || c != s[si++]) return false;
  223. }
  224. }
  225. return (si == _slen);
  226. }
  227. /**
  228. * Retrieves the named object contained in repository
  229. * from the given objectname.
  230. */
  231. private NamedObject retrieveNamedObject(ObjectName name) {
  232. // No patterns inside reposit
  233. if (name.isPattern() == true) return null;
  234. // Extract the domain name.
  235. String dom= name.getDomain().intern();
  236. // Default domain case
  237. if (dom.length() == 0) {
  238. dom = domain;
  239. }
  240. Object tmp_object = domainTb.get(dom);
  241. if (tmp_object == null) {
  242. return null; // No domain containing registered object names
  243. }
  244. // If name exists in repository, we will get it now
  245. Hashtable moiTb= (Hashtable) tmp_object;
  246. Object o = moiTb.get(name.getCanonicalKeyPropertyListString());
  247. if (o != null ) {
  248. return (NamedObject) o;
  249. }
  250. else return null;
  251. }
  252. // Private methods <=============================================
  253. // Protected methods --------------------------------------------->
  254. // Protected methods <=============================================
  255. // Public methods --------------------------------------------->
  256. /**
  257. * Construct a new repository with the given default domain.
  258. *
  259. */
  260. public RepositorySupport(String domain) {
  261. domainTb= new Hashtable(5);
  262. if (domain != null && domain.length() != 0)
  263. this.domain = domain;
  264. else
  265. this.domain = ServiceName.DOMAIN;
  266. // Creates an new hastable for the default domain
  267. domainTb.put(this.domain.intern(), new Hashtable());
  268. // ------------------------------
  269. // ------------------------------
  270. }
  271. /**
  272. * The purpose of this method is to provide a unified way to provide
  273. * whatever configuration information is needed by the specific
  274. * underlying implementation of the repository.
  275. *
  276. * @param configParameters An list containing the configuration
  277. * parameters needed by the specific Repository Service
  278. * implementation.
  279. */
  280. public void setConfigParameters(ArrayList configParameters) {
  281. return;
  282. }
  283. /**
  284. * Returns the list of domains in which any MBean is currently
  285. * registered.
  286. *
  287. * @since.unbundled JMX RI 1.2
  288. */
  289. public String[] getDomains() {
  290. final ArrayList result;
  291. synchronized(domainTb) {
  292. // Temporary list
  293. result = new ArrayList(domainTb.size());
  294. // Loop over all domains
  295. for (Enumeration e = domainTb.keys();e.hasMoreElements();) {
  296. // key = domain name
  297. final String key = (String)e.nextElement();
  298. if (key == null) continue;
  299. // If no MBean in domain continue
  300. final Hashtable t = (Hashtable)domainTb.get(key);
  301. if (t == null || t.size()==0) continue;
  302. // Some MBean are registered => add to result.
  303. result.add(key);
  304. }
  305. }
  306. // Make an array from result.
  307. return (String[]) result.toArray(new String[result.size()]);
  308. }
  309. /**
  310. * Indicates whether or not the Repository Service supports filtering. If
  311. * the Repository Service does not support filtering, the MBean Server
  312. * will perform filtering.
  313. *
  314. * @return true if filtering is supported, false otherwise.
  315. */
  316. public boolean isFiltering() {
  317. // Let the MBeanServer perform the filtering !
  318. return false;
  319. }
  320. /**
  321. * Stores an MBean associated with its object name in the repository.
  322. *
  323. *@param object MBean to be stored in the repository.
  324. *@param name MBean object name.
  325. *
  326. */
  327. public void addMBean(final Object object, ObjectName name)
  328. throws InstanceAlreadyExistsException {
  329. if (isTraceOn()) {
  330. trace("addMBean", "name=" + name);
  331. }
  332. // Extract the domain name.
  333. String dom = name.getDomain().intern();
  334. boolean to_default_domain = false;
  335. // Set domain to default if domain is empty and not already set
  336. if (dom.length() == 0) {
  337. try {
  338. name = new ObjectName(domain + name.toString());
  339. } catch (MalformedObjectNameException e) {
  340. if (isDebugOn()) {
  341. debug("addMBean",
  342. "Unexpected MalformedObjectNameException");
  343. }
  344. }
  345. }
  346. // Do we have default domain ?
  347. if (dom == domain) {
  348. to_default_domain = true;
  349. dom = domain;
  350. } else {
  351. to_default_domain = false;
  352. }
  353. // ------------------------------
  354. // ------------------------------
  355. // Validate name for an object
  356. if (name.isPattern() == true) {
  357. throw new RuntimeOperationsException(
  358. new IllegalArgumentException("Repository: cannot add mbean for pattern name " + name.toString()));
  359. }
  360. // Domain cannot be JMImplementation if entry does not exists
  361. if ( !to_default_domain &&
  362. dom.equals("JMImplementation") &&
  363. domainTb.containsKey("JMImplementation")) {
  364. throw new RuntimeOperationsException(
  365. new IllegalArgumentException(
  366. "Repository: domain name cannot be JMImplementation"));
  367. }
  368. // If domain not already exists, add it to the hash table
  369. final Hashtable moiTb= (Hashtable) domainTb.get(dom);
  370. if (moiTb == null) {
  371. addNewDomMoi(object, dom, name);
  372. return;
  373. }
  374. else {
  375. // Add instance if not already present
  376. String cstr = name.getCanonicalKeyPropertyListString();
  377. Object elmt= moiTb.get(cstr);
  378. if (elmt != null) {
  379. throw new InstanceAlreadyExistsException(name.toString());
  380. } else {
  381. nbElements++;
  382. moiTb.put(cstr, new NamedObject(name, object));
  383. }
  384. }
  385. }
  386. /**
  387. * Checks whether an MBean of the name specified is already stored in
  388. * the repository.
  389. *
  390. * @param name name of the MBean to find.
  391. *
  392. * @return true if the MBean is stored in the repository,
  393. * false otherwise.
  394. *
  395. */
  396. public boolean contains(ObjectName name) {
  397. if (isTraceOn()) {
  398. trace("contains", "name=" + name);
  399. }
  400. return (retrieveNamedObject(name) != null);
  401. }
  402. /**
  403. * Retrieves the MBean of the name specified from the repository. The
  404. * object name must match exactly.
  405. *
  406. * @param name name of the MBean to retrieve.
  407. *
  408. * @return The retrieved MBean if it is contained in the repository,
  409. * null otherwise.
  410. *
  411. */
  412. public Object retrieve(ObjectName name) {
  413. // ------------------------------
  414. // ------------------------------
  415. if (isTraceOn()) {
  416. trace("retrieve", "name=" + name);
  417. }
  418. // Calls internal retrieve method to get the named object
  419. NamedObject no = retrieveNamedObject(name);
  420. if (no == null) return null;
  421. else return no.getObject();
  422. }
  423. /**
  424. * Selects and retrieves the list of MBeans whose names match the specified
  425. * object name pattern and which match the specified query expression
  426. * (optionally).
  427. *
  428. * @param pattern The name of the MBean(s) to retrieve - may be a specific
  429. * object or a name pattern allowing multiple MBeans to be selected.
  430. * @param query query expression to apply when selecting objects - this
  431. * parameter will be ignored when the Repository Service does not
  432. * support filtering.
  433. *
  434. * @return The list of MBeans selected. There may be zero, one or many
  435. * MBeans returned in the set.
  436. *
  437. */
  438. public Set query(ObjectName pattern, QueryExp query) {
  439. // ------------------------------
  440. // ------------------------------
  441. ObjectNamePattern on_pattern = null; // intermediate Object name pattern for performance
  442. final HashSet result = new HashSet();
  443. // The following filter cases are considered :
  444. // null, "", "*:*"" : names in all domains
  445. // ":*" : names in defaultDomain
  446. // "domain:*" : names in the specified domain
  447. // "domain:[key=value], *"
  448. // Surely one of the most frequent case ... query on the whole world
  449. ObjectName name = null;
  450. if (pattern == null ||
  451. pattern.getCanonicalName().length() == 0 ||
  452. pattern.equals(_WholeWordQueryObjectName))
  453. name = _WholeWordQueryObjectName;
  454. else name = pattern;
  455. // If pattern is not a pattern, retrieve this mbean !
  456. if (!name.isPattern()) {
  457. final NamedObject no = retrieveNamedObject(name);
  458. if (no != null) result.add(no);
  459. return result;
  460. }
  461. // all names in all domains
  462. if (name == _WholeWordQueryObjectName) {
  463. synchronized(domainTb) {
  464. for(final Enumeration e = domainTb.elements();
  465. e.hasMoreElements();) {
  466. final Hashtable moiTb = (Hashtable) e.nextElement();
  467. result.addAll(moiTb.values());
  468. }
  469. }
  470. return result;
  471. }
  472. String canonical_key_property_list_string = name.getCanonicalKeyPropertyListString();
  473. // all names in default domain
  474. //
  475. // DF: fix 4618986 - take into account the case where the
  476. // property list is not empty.
  477. //
  478. if (name.getDomain().length() == 0) {
  479. final Hashtable moiTb = (Hashtable) domainTb.get(domain);
  480. if (canonical_key_property_list_string.length() == 0) {
  481. result.addAll(moiTb.values());
  482. } else {
  483. if (on_pattern == null)
  484. on_pattern = new ObjectNamePattern(name);
  485. addAllMatching(moiTb,result,on_pattern);
  486. }
  487. return result;
  488. }
  489. // Pattern matching in the domain name (*, ?)
  490. synchronized (domainTb) {
  491. char[] dom2Match = name.getDomain().toCharArray();
  492. String nextDomain;
  493. char [] theDom;
  494. for (final Enumeration enumi = domainTb.keys(); enumi.hasMoreElements();) {
  495. nextDomain = (String) enumi.nextElement();
  496. theDom = nextDomain.toCharArray();
  497. if (wildmatch(theDom, dom2Match, 0, 0)) {
  498. final Hashtable moiTb =
  499. (Hashtable) domainTb.get(nextDomain);
  500. if (canonical_key_property_list_string.length() == 0)
  501. result.addAll(moiTb.values());
  502. else {
  503. if (on_pattern == null)
  504. on_pattern = new ObjectNamePattern(name);
  505. addAllMatching(moiTb,result,on_pattern);
  506. }
  507. }
  508. }
  509. }
  510. return result;
  511. }
  512. /**
  513. * Removes an MBean from the repository.
  514. *
  515. * @param name name of the MBean to remove.
  516. *
  517. * @exception InstanceNotFoundException The MBean does not exist in
  518. * the repository.
  519. */
  520. public void remove(final ObjectName name)
  521. throws InstanceNotFoundException {
  522. // Debugging stuff
  523. if (isTraceOn()) {
  524. trace("remove", "name=" + name);
  525. }
  526. // Extract domain name.
  527. String dom= name.getDomain().intern();
  528. // Default domain case
  529. if (dom.length() == 0) dom = domain;
  530. // Find the domain subtable
  531. Object tmp_object = domainTb.get(dom);
  532. if (tmp_object == null) {
  533. throw new InstanceNotFoundException(name.toString());
  534. }
  535. // Remove the corresponding element
  536. Hashtable moiTb= (Hashtable) tmp_object;
  537. if (moiTb.remove(name.getCanonicalKeyPropertyListString()) == null) {
  538. throw new InstanceNotFoundException(name.toString());
  539. }
  540. // We removed it !
  541. nbElements--;
  542. // No more object for this domain, we remove this domain hashtable
  543. if (moiTb.isEmpty()) {
  544. domainTb.remove(dom);
  545. // set a new default domain table (always present)
  546. // need to reinstantiate a hashtable because of possible
  547. // big buckets array size inside table, never cleared,
  548. // thus the new !
  549. if (dom == domain)
  550. domainTb.put(domain, new Hashtable());
  551. }
  552. }
  553. /**
  554. * Gets the number of MBeans stored in the repository.
  555. *
  556. * @return Number of MBeans.
  557. */
  558. public Integer getCount() {
  559. return new Integer(nbElements);
  560. }
  561. /**
  562. * Gets the name of the domain currently used by default in the
  563. * repository.
  564. *
  565. * @return A string giving the name of the default domain name.
  566. */
  567. public String getDefaultDomain() {
  568. return domain;
  569. }
  570. }