History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/com/sun/jmx/mbeanserver/Repository.java (1 of 18352 files). Show all

Unified · Split

java/pkg/com/sun/jmx/mbeanserver/Repository.java+678−0
@@ -0,0 +1,678 @@
1+/*
2+ * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package com.sun.jmx.mbeanserver;
27+
28+import com.sun.jmx.defaults.ServiceName;
29+import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
30+
31+import java.util.ArrayList;
32+import java.util.Collections;
33+import java.util.HashMap;
34+import java.util.HashSet;
35+import java.util.List;
36+import java.util.concurrent.locks.ReentrantReadWriteLock;
37+import java.util.logging.Level;
38+import java.util.Map;
39+import java.util.Set;
40+import javax.management.DynamicMBean;
41+import javax.management.InstanceAlreadyExistsException;
42+import javax.management.InstanceNotFoundException;
43+import javax.management.ObjectName;
44+import javax.management.QueryExp;
45+import javax.management.RuntimeOperationsException;
46+
47+/**
48+ * This repository does not support persistency.
49+ *
50+ * @since 1.5
51+ */
52+public class Repository {
53+
54+ /**
55+ * An interface that allows the caller to get some control
56+ * over the registration.
57+ * @see #addMBean
58+ * @see #remove
59+ */
60+ public interface RegistrationContext {
61+ /**
62+ * Called by {@link #addMBean}.
63+ * Can throw a RuntimeOperationsException to cancel the
64+ * registration.
65+ */
66+ public void registering();
67+
68+ /**
69+ * Called by {@link #remove}.
70+ * Any exception thrown by this method will be ignored.
71+ */
72+ public void unregistered();
73+ }
74+
75+ // Private fields -------------------------------------------->
76+
77+ /**
78+ * The structure for storing the objects is very basic.
79+ * A Hashtable is used for storing the different domains
80+ * For each domain, a hashtable contains the instances with
81+ * canonical key property list string as key and named object
82+ * aggregated from given object name and mbean instance as value.
83+ */
84+ private final Map<String,Map<String,NamedObject>> domainTb;
85+
86+ /**
87+ * Number of elements contained in the Repository
88+ */
89+ private volatile int nbElements = 0;
90+
91+ /**
92+ * Domain name of the server the repository is attached to.
93+ * It is quicker to store the information in the repository rather
94+ * than querying the framework each time the info is required.
95+ */
96+ private final String domain;
97+
98+ /**
99+ * We use a global reentrant read write lock to protect the repository.
100+ * This seems safer and more efficient: we are using Maps of Maps,
101+ * Guaranteing consistency while using Concurent objects at each level
102+ * may be more difficult.
103+ **/
104+ private final ReentrantReadWriteLock lock;
105+
106+ // Private fields <=============================================
107+
108+ // Private methods --------------------------------------------->
109+
110+ /* This class is used to match an ObjectName against a pattern. */
111+ private final static class ObjectNamePattern {
112+ private final String[] keys;
113+ private final String[] values;
114+ private final String properties;
115+ private final boolean isPropertyListPattern;
116+ private final boolean isPropertyValuePattern;
117+
118+ /**
119+ * The ObjectName pattern against which ObjectNames are matched.
120+ **/
121+ public final ObjectName pattern;
122+
123+ /**
124+ * Builds a new ObjectNamePattern object from an ObjectName pattern.
125+ * @param pattern The ObjectName pattern under examination.
126+ **/
127+ public ObjectNamePattern(ObjectName pattern) {
128+ this(pattern.isPropertyListPattern(),
129+ pattern.isPropertyValuePattern(),
130+ pattern.getCanonicalKeyPropertyListString(),
131+ pattern.getKeyPropertyList(),
132+ pattern);
133+ }
134+
135+ /**
136+ * Builds a new ObjectNamePattern object from an ObjectName pattern
137+ * constituents.
138+ * @param propertyListPattern pattern.isPropertyListPattern().
139+ * @param propertyValuePattern pattern.isPropertyValuePattern().
140+ * @param canonicalProps pattern.getCanonicalKeyPropertyListString().
141+ * @param keyPropertyList pattern.getKeyPropertyList().
142+ * @param pattern The ObjectName pattern under examination.
143+ **/
144+ ObjectNamePattern(boolean propertyListPattern,
145+ boolean propertyValuePattern,
146+ String canonicalProps,
147+ Map<String,String> keyPropertyList,
148+ ObjectName pattern) {
149+ this.isPropertyListPattern = propertyListPattern;
150+ this.isPropertyValuePattern = propertyValuePattern;
151+ this.properties = canonicalProps;
152+ final int len = keyPropertyList.size();
153+ this.keys = new String[len];
154+ this.values = new String[len];
155+ int i = 0;
156+ for (Map.Entry<String,String> entry : keyPropertyList.entrySet()) {
157+ keys[i] = entry.getKey();
158+ values[i] = entry.getValue();
159+ i++;
160+ }
161+ this.pattern = pattern;
162+ }
163+
164+ /**
165+ * Return true if the given ObjectName matches the ObjectName pattern
166+ * for which this object has been built.
167+ * WARNING: domain name is not considered here because it is supposed
168+ * not to be wildcard when called. PropertyList is also
169+ * supposed not to be zero-length.
170+ * @param name The ObjectName we want to match against the pattern.
171+ * @return true if <code>name</code> matches the pattern.
172+ **/
173+ public boolean matchKeys(ObjectName name) {
174+ // If key property value pattern but not key property list
175+ // pattern, then the number of key properties must be equal
176+ //
177+ if (isPropertyValuePattern &&
178+ !isPropertyListPattern &&
179+ (name.getKeyPropertyList().size() != keys.length))
180+ return false;
181+
182+ // If key property value pattern or key property list pattern,
183+ // then every property inside pattern should exist in name
184+ //
185+ if (isPropertyValuePattern || isPropertyListPattern) {
186+ for (int i = keys.length - 1; i >= 0 ; i--) {
187+ // Find value in given object name for key at current
188+ // index in receiver
189+ //
190+ String v = name.getKeyProperty(keys[i]);
191+ // Did we find a value for this key ?
192+ //
193+ if (v == null) return false;
194+ // If this property is ok (same key, same value), go to next
195+ //
196+ if (isPropertyValuePattern &&
197+ pattern.isPropertyValuePattern(keys[i])) {
198+ // wildmatch key property values
199+ // values[i] is the pattern;
200+ // v is the string
201+ if (Util.wildmatch(v,values[i]))
202+ continue;
203+ else
204+ return false;
205+ }
206+ if (v.equals(values[i])) continue;
207+ return false;
208+ }
209+ return true;
210+ }
211+
212+ // If no pattern, then canonical names must be equal
213+ //
214+ final String p1 = name.getCanonicalKeyPropertyListString();
215+ final String p2 = properties;
216+ return (p1.equals(p2));
217+ }
218+ }
219+
220+ /**
221+ * Add all the matching objects from the given hashtable in the
222+ * result set for the given ObjectNamePattern
223+ * Do not check whether the domains match (only check for matching
224+ * key property lists - see <i>matchKeys()</i>)
225+ **/
226+ private void addAllMatching(final Map<String,NamedObject> moiTb,
227+ final Set<NamedObject> result,
228+ final ObjectNamePattern pattern) {
229+ synchronized (moiTb) {
230+ for (NamedObject no : moiTb.values()) {
231+ final ObjectName on = no.getName();
232+ // if all couples (property, value) are contained
233+ if (pattern.matchKeys(on)) result.add(no);
234+ }
235+ }
236+ }
237+
238+ private void addNewDomMoi(final DynamicMBean object,
239+ final String dom,
240+ final ObjectName name,
241+ final RegistrationContext context) {
242+ final Map<String,NamedObject> moiTb =
243+ new HashMap<String,NamedObject>();
244+ final String key = name.getCanonicalKeyPropertyListString();
245+ addMoiToTb(object,name,key,moiTb,context);
246+ domainTb.put(dom, moiTb);
247+ nbElements++;
248+ }
249+
250+ private void registering(RegistrationContext context) {
251+ if (context == null) return;
252+ try {
253+ context.registering();
254+ } catch (RuntimeOperationsException x) {
255+ throw x;
256+ } catch (RuntimeException x) {
257+ throw new RuntimeOperationsException(x);
258+ }
259+ }
260+
261+ private void unregistering(RegistrationContext context, ObjectName name) {
262+ if (context == null) return;
263+ try {
264+ context.unregistered();
265+ } catch (Exception x) {
266+ // shouldn't come here...
267+ MBEANSERVER_LOGGER.log(Level.FINE,
268+ "Unexpected exception while unregistering "+name,
269+ x);
270+ }
271+ }
272+
273+ private void addMoiToTb(final DynamicMBean object,
274+ final ObjectName name,
275+ final String key,
276+ final Map<String,NamedObject> moiTb,
277+ final RegistrationContext context) {
278+ registering(context);
279+ moiTb.put(key,new NamedObject(name, object));
280+ }
281+
282+ /**
283+ * Retrieves the named object contained in repository
284+ * from the given objectname.
285+ */
286+ private NamedObject retrieveNamedObject(ObjectName name) {
287+
288+ // No patterns inside reposit
289+ if (name.isPattern()) return null;
290+
291+ // Extract the domain name.
292+ String dom = name.getDomain().intern();
293+
294+ // Default domain case
295+ if (dom.length() == 0) {
296+ dom = domain;
297+ }
298+
299+ Map<String,NamedObject> moiTb = domainTb.get(dom);
300+ if (moiTb == null) {
301+ return null; // No domain containing registered object names
302+ }
303+
304+ return moiTb.get(name.getCanonicalKeyPropertyListString());
305+ }
306+
307+ // Private methods <=============================================
308+
309+ // Protected methods --------------------------------------------->
310+
311+ // Protected methods <=============================================
312+
313+ // Public methods --------------------------------------------->
314+
315+ /**
316+ * Construct a new repository with the given default domain.
317+ */
318+ public Repository(String domain) {
319+ this(domain,true);
320+ }
321+
322+ /**
323+ * Construct a new repository with the given default domain.
324+ */
325+ public Repository(String domain, boolean fairLock) {
326+ lock = new ReentrantReadWriteLock(fairLock);
327+
328+ domainTb = new HashMap<String,Map<String,NamedObject>>(5);
329+
330+ if (domain != null && domain.length() != 0)
331+ this.domain = domain.intern(); // we use == domain later on...
332+ else
333+ this.domain = ServiceName.DOMAIN;
334+
335+ // Creates a new hashtable for the default domain
336+ domainTb.put(this.domain, new HashMap<String,NamedObject>());
337+ }
338+
339+ /**
340+ * Returns the list of domains in which any MBean is currently
341+ * registered.
342+ *
343+ */
344+ public String[] getDomains() {
345+
346+ lock.readLock().lock();
347+ final List<String> result;
348+ try {
349+ // Temporary list
350+ result = new ArrayList<String>(domainTb.size());
351+ for (Map.Entry<String,Map<String,NamedObject>> entry :
352+ domainTb.entrySet()) {
353+ // Skip domains that are in the table but have no
354+ // MBean registered in them
355+ // in particular the default domain may be like this
356+ Map<String,NamedObject> t = entry.getValue();
357+ if (t != null && t.size() != 0)
358+ result.add(entry.getKey());
359+ }
360+ } finally {
361+ lock.readLock().unlock();
362+ }
363+
364+ // Make an array from result.
365+ return result.toArray(new String[result.size()]);
366+ }
367+
368+ /**
369+ * Stores an MBean associated with its object name in the repository.
370+ *
371+ * @param object MBean to be stored in the repository.
372+ * @param name MBean object name.
373+ * @param context A registration context. If non null, the repository
374+ * will call {@link RegistrationContext#registering()
375+ * context.registering()} from within the repository
376+ * lock, when it has determined that the {@code object}
377+ * can be stored in the repository with that {@code name}.
378+ * If {@link RegistrationContext#registering()
379+ * context.registering()} throws an exception, the
380+ * operation is abandonned, the MBean is not added to the
381+ * repository, and a {@link RuntimeOperationsException}
382+ * is thrown.
383+ */
384+ public void addMBean(final DynamicMBean object, ObjectName name,
385+ final RegistrationContext context)
386+ throws InstanceAlreadyExistsException {
387+
388+ if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
389+ MBEANSERVER_LOGGER.logp(Level.FINER, Repository.class.getName(),
390+ "addMBean", "name = " + name);
391+ }
392+
393+ // Extract the domain name.
394+ String dom = name.getDomain().intern();
395+ boolean to_default_domain = false;
396+
397+ // Set domain to default if domain is empty and not already set
398+ if (dom.length() == 0)
399+ name = Util.newObjectName(domain + name.toString());
400+
401+ // Do we have default domain ?
402+ if (dom == domain) { // ES: OK (dom & domain are interned)
403+ to_default_domain = true;
404+ dom = domain;
405+ } else {
406+ to_default_domain = false;
407+ }
408+
409+ // Validate name for an object
410+ if (name.isPattern()) {
411+ throw new RuntimeOperationsException(
412+ new IllegalArgumentException("Repository: cannot add mbean for " +
413+ "pattern name " + name.toString()));
414+ }
415+
416+ lock.writeLock().lock();
417+ try {
418+ // Domain cannot be JMImplementation if entry does not exist
419+ if ( !to_default_domain &&
420+ dom.equals("JMImplementation") &&
421+ domainTb.containsKey("JMImplementation")) {
422+ throw new RuntimeOperationsException(
423+ new IllegalArgumentException(
424+ "Repository: domain name cannot be JMImplementation"));
425+ }
426+
427+ // If domain does not already exist, add it to the hash table
428+ final Map<String,NamedObject> moiTb = domainTb.get(dom);
429+ if (moiTb == null) {
430+ addNewDomMoi(object, dom, name, context);
431+ return;
432+ } else {
433+ // Add instance if not already present
434+ String cstr = name.getCanonicalKeyPropertyListString();
435+ NamedObject elmt= moiTb.get(cstr);
436+ if (elmt != null) {
437+ throw new InstanceAlreadyExistsException(name.toString());
438+ } else {
439+ nbElements++;
440+ addMoiToTb(object,name,cstr,moiTb,context);
441+ }
442+ }
443+
444+ } finally {
445+ lock.writeLock().unlock();
446+ }
447+ }
448+
449+ /**
450+ * Checks whether an MBean of the name specified is already stored in
451+ * the repository.
452+ *
453+ * @param name name of the MBean to find.
454+ *
455+ * @return true if the MBean is stored in the repository,
456+ * false otherwise.
457+ */
458+ public boolean contains(ObjectName name) {
459+ if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
460+ MBEANSERVER_LOGGER.logp(Level.FINER, Repository.class.getName(),
461+ "contains", " name = " + name);
462+ }
463+ lock.readLock().lock();
464+ try {
465+ return (retrieveNamedObject(name) != null);
466+ } finally {
467+ lock.readLock().unlock();
468+ }
469+ }
470+
471+ /**
472+ * Retrieves the MBean of the name specified from the repository. The
473+ * object name must match exactly.
474+ *
475+ * @param name name of the MBean to retrieve.
476+ *
477+ * @return The retrieved MBean if it is contained in the repository,
478+ * null otherwise.
479+ */
480+ public DynamicMBean retrieve(ObjectName name) {
481+ if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
482+ MBEANSERVER_LOGGER.logp(Level.FINER, Repository.class.getName(),
483+ "retrieve", "name = " + name);
484+ }
485+
486+ // Calls internal retrieve method to get the named object
487+ lock.readLock().lock();
488+ try {
489+ NamedObject no = retrieveNamedObject(name);
490+ if (no == null) return null;
491+ else return no.getObject();
492+ } finally {
493+ lock.readLock().unlock();
494+ }
495+ }
496+
497+ /**
498+ * Selects and retrieves the list of MBeans whose names match the specified
499+ * object name pattern and which match the specified query expression
500+ * (optionally).
501+ *
502+ * @param pattern The name of the MBean(s) to retrieve - may be a specific
503+ * object or a name pattern allowing multiple MBeans to be selected.
504+ * @param query query expression to apply when selecting objects - this
505+ * parameter will be ignored when the Repository Service does not
506+ * support filtering.
507+ *
508+ * @return The list of MBeans selected. There may be zero, one or many
509+ * MBeans returned in the set.
510+ */
511+ public Set<NamedObject> query(ObjectName pattern, QueryExp query) {
512+
513+ final Set<NamedObject> result = new HashSet<NamedObject>();
514+
515+ // The following filter cases are considered:
516+ // null, "", "*:*" : names in all domains
517+ // ":*", ":[key=value],*" : names in defaultDomain
518+ // "domain:*", "domain:[key=value],*" : names in the specified domain
519+
520+ // Surely one of the most frequent cases ... query on the whole world
521+ ObjectName name;
522+ if (pattern == null ||
523+ pattern.getCanonicalName().length() == 0 ||
524+ pattern.equals(ObjectName.WILDCARD))
525+ name = ObjectName.WILDCARD;
526+ else name = pattern;
527+
528+ lock.readLock().lock();
529+ try {
530+
531+ // If pattern is not a pattern, retrieve this mbean !
532+ if (!name.isPattern()) {
533+ final NamedObject no = retrieveNamedObject(name);
534+ if (no != null) result.add(no);
535+ return result;
536+ }
537+
538+ // All names in all domains
539+ if (name == ObjectName.WILDCARD) {
540+ for (Map<String,NamedObject> moiTb : domainTb.values()) {
541+ result.addAll(moiTb.values());
542+ }
543+ return result;
544+ }
545+
546+ final String canonical_key_property_list_string =
547+ name.getCanonicalKeyPropertyListString();
548+ final boolean allNames =
549+ (canonical_key_property_list_string.length()==0);
550+ final ObjectNamePattern namePattern =
551+ (allNames?null:new ObjectNamePattern(name));
552+
553+ // All names in default domain
554+ if (name.getDomain().length() == 0) {
555+ final Map<String,NamedObject> moiTb = domainTb.get(domain);
556+ if (allNames)
557+ result.addAll(moiTb.values());
558+ else
559+ addAllMatching(moiTb, result, namePattern);
560+ return result;
561+ }
562+
563+ if (!name.isDomainPattern()) {
564+ final Map<String,NamedObject> moiTb = domainTb.get(name.getDomain());
565+ if (moiTb == null) return Collections.emptySet();
566+ if (allNames)
567+ result.addAll(moiTb.values());
568+ else
569+ addAllMatching(moiTb, result, namePattern);
570+ return result;
571+ }
572+
573+ // Pattern matching in the domain name (*, ?)
574+ final String dom2Match = name.getDomain();
575+ for (String dom : domainTb.keySet()) {
576+ if (Util.wildmatch(dom, dom2Match)) {
577+ final Map<String,NamedObject> moiTb = domainTb.get(dom);
578+ if (allNames)
579+ result.addAll(moiTb.values());
580+ else
581+ addAllMatching(moiTb, result, namePattern);
582+ }
583+ }
584+ return result;
585+ } finally {
586+ lock.readLock().unlock();
587+ }
588+ }
589+
590+ /**
591+ * Removes an MBean from the repository.
592+ *
593+ * @param name name of the MBean to remove.
594+ * @param context A registration context. If non null, the repository
595+ * will call {@link RegistrationContext#unregistered()
596+ * context.unregistered()} from within the repository
597+ * lock, just after the mbean associated with
598+ * {@code name} is removed from the repository.
599+ * If {@link RegistrationContext#unregistered()
600+ * context.unregistered()} is not expected to throw any
601+ * exception. If it does, the exception is logged
602+ * and swallowed.
603+ *
604+ * @exception InstanceNotFoundException The MBean does not exist in
605+ * the repository.
606+ */
607+ public void remove(final ObjectName name,
608+ final RegistrationContext context)
609+ throws InstanceNotFoundException {
610+
611+ // Debugging stuff
612+ if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
613+ MBEANSERVER_LOGGER.logp(Level.FINER, Repository.class.getName(),
614+ "remove", "name = " + name);
615+ }
616+
617+ // Extract domain name.
618+ String dom= name.getDomain().intern();
619+
620+ // Default domain case
621+ if (dom.length() == 0) dom = domain;
622+
623+ lock.writeLock().lock();
624+ try {
625+ // Find the domain subtable
626+ final Map<String,NamedObject> moiTb = domainTb.get(dom);
627+ if (moiTb == null) {
628+ throw new InstanceNotFoundException(name.toString());
629+ }
630+
631+ // Remove the corresponding element
632+ if (moiTb.remove(name.getCanonicalKeyPropertyListString())==null) {
633+ throw new InstanceNotFoundException(name.toString());
634+ }
635+
636+ // We removed it !
637+ nbElements--;
638+
639+ // No more object for this domain, we remove this domain hashtable
640+ if (moiTb.isEmpty()) {
641+ domainTb.remove(dom);
642+
643+ // set a new default domain table (always present)
644+ // need to reinstantiate a hashtable because of possible
645+ // big buckets array size inside table, never cleared,
646+ // thus the new !
647+ if (dom == domain) // ES: OK dom and domain are interned.
648+ domainTb.put(domain, new HashMap<String,NamedObject>());
649+ }
650+
651+ unregistering(context,name);
652+
653+ } finally {
654+ lock.writeLock().unlock();
655+ }
656+ }
657+
658+ /**
659+ * Gets the number of MBeans stored in the repository.
660+ *
661+ * @return Number of MBeans.
662+ */
663+ public Integer getCount() {
664+ return nbElements;
665+ }
666+
667+ /**
668+ * Gets the name of the domain currently used by default in the
669+ * repository.
670+ *
671+ * @return A string giving the name of the default domain name.
672+ */
673+ public String getDefaultDomain() {
674+ return domain;
675+ }
676+
677+ // Public methods <=============================================
678+}