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
26package com.sun.jmx.mbeanserver;
27
28import com.sun.jmx.defaults.ServiceName;
29import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
30
31import java.util.ArrayList;
32import java.util.Collections;
33import java.util.HashMap;
34import java.util.HashSet;
35import java.util.List;
36import java.util.concurrent.locks.ReentrantReadWriteLock;
37import java.util.logging.Level;
38import java.util.Map;
39import java.util.Set;
40import javax.management.DynamicMBean;
41import javax.management.InstanceAlreadyExistsException;
42import javax.management.InstanceNotFoundException;
43import javax.management.ObjectName;
44import javax.management.QueryExp;
45import javax.management.RuntimeOperationsException;
46
47/**
48 * This repository does not support persistency.
49 *
50 * @since 1.5
51 */
52public 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}