1 /*
2  * Copyright (c) 2005, 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 static com.sun.jmx.mbeanserver.Util.*;
29 
30 import java.util.Iterator;
31 import java.util.Set;
32 
33 import javax.management.InstanceAlreadyExistsException;
34 import javax.management.JMX;
35 import javax.management.MBeanServer;
36 import javax.management.NotCompliantMBeanException;
37 import javax.management.ObjectName;
38 
39 /**
40  * Base class for MXBeans.
41  *
42  * @since 1.6
43  */
44 public class MXBeanSupport extends MBeanSupport<ConvertingMethod> {
45 
46     /**
47        <p>Construct an MXBean that wraps the given resource using the
48        given MXBean interface.</p>
49 
50        @param resource the underlying resource for the new MXBean.
51 
52        @param mxbeanInterface the interface to be used to determine
53        the MXBean's management interface.
54 
55        @param <T> a type parameter that allows the compiler to check
56        that {@code resource} implements {@code mxbeanInterface},
57        provided that {@code mxbeanInterface} is a class constant like
58        {@code SomeMXBean.class}.
59 
60        @throws IllegalArgumentException if {@code resource} is null or
61        if it does not implement the class {@code mxbeanInterface} or if
62        that class is not a valid MXBean interface.
63     */
MXBeanSupport(T resource, Class<T> mxbeanInterface)64     public <T> MXBeanSupport(T resource, Class<T> mxbeanInterface)
65             throws NotCompliantMBeanException {
66         super(resource, mxbeanInterface);
67     }
68 
69     @Override
getMBeanIntrospector()70     MBeanIntrospector<ConvertingMethod> getMBeanIntrospector() {
71         return MXBeanIntrospector.getInstance();
72     }
73 
74     @Override
getCookie()75     Object getCookie() {
76         return mxbeanLookup;
77     }
78 
findMXBeanInterface(Class<T> resourceClass)79     static <T> Class<? super T> findMXBeanInterface(Class<T> resourceClass) {
80         if (resourceClass == null)
81             throw new IllegalArgumentException("Null resource class");
82         final Set<Class<?>> intfs = transitiveInterfaces(resourceClass);
83         final Set<Class<?>> candidates = newSet();
84         for (Class<?> intf : intfs) {
85             if (JMX.isMXBeanInterface(intf))
86                 candidates.add(intf);
87         }
88     reduce:
89         while (candidates.size() > 1) {
90             for (Class<?> intf : candidates) {
91                 for (Iterator<Class<?>> it = candidates.iterator(); it.hasNext();
92                     ) {
93                     final Class<?> intf2 = it.next();
94                     if (intf != intf2 && intf2.isAssignableFrom(intf)) {
95                         it.remove();
96                         continue reduce;
97                     }
98                 }
99             }
100             final String msg =
101                 "Class " + resourceClass.getName() + " implements more than " +
102                 "one MXBean interface: " + candidates;
103             throw new IllegalArgumentException(msg);
104         }
105         if (candidates.iterator().hasNext()) {
106             return Util.cast(candidates.iterator().next());
107         } else {
108             final String msg =
109                 "Class " + resourceClass.getName() +
110                 " is not a JMX compliant MXBean";
111             throw new IllegalArgumentException(msg);
112         }
113     }
114 
115     /* Return all interfaces inherited by this class, directly or
116      * indirectly through the parent class and interfaces.
117      */
transitiveInterfaces(Class<?> c)118     private static Set<Class<?>> transitiveInterfaces(Class<?> c) {
119         Set<Class<?>> set = newSet();
120         transitiveInterfaces(c, set);
121         return set;
122     }
transitiveInterfaces(Class<?> c, Set<Class<?>> intfs)123     private static void transitiveInterfaces(Class<?> c, Set<Class<?>> intfs) {
124         if (c == null)
125             return;
126         if (c.isInterface())
127             intfs.add(c);
128         transitiveInterfaces(c.getSuperclass(), intfs);
129         for (Class<?> sup : c.getInterfaces())
130             transitiveInterfaces(sup, intfs);
131     }
132 
133     /*
134      * The sequence of events for tracking inter-MXBean references is
135      * relatively complicated.  We use the magical preRegister2 method
136      * which the MBeanServer knows about.  The steps during registration
137      * are:
138      * (1) Call user preRegister, if any.  If exception, abandon.
139      * (2) Call preRegister2 and hence this register method.  If exception,
140      * call postRegister(false) and abandon.
141      * (3) Try to register the MBean.  If exception, call registerFailed()
142      * which will call the unregister method.  (Also call postRegister(false).)
143      * (4) If we get this far, we can call postRegister(true).
144      *
145      * When we are wrapped in an instance of javax.management.StandardMBean,
146      * things are simpler.  That class calls this method from its preRegister,
147      * and propagates any exception.  There is no user preRegister in this case.
148      * If this method succeeds but registration subsequently fails,
149      * StandardMBean calls unregister from its postRegister(false) method.
150      */
151     @Override
register(MBeanServer server, ObjectName name)152     public void register(MBeanServer server, ObjectName name)
153             throws InstanceAlreadyExistsException {
154         if (name == null)
155             throw new IllegalArgumentException("Null object name");
156         // eventually we could have some logic to supply a default name
157 
158         synchronized (lock) {
159             this.mxbeanLookup = MXBeanLookup.lookupFor(server);
160             this.mxbeanLookup.addReference(name, getResource());
161             this.objectName = name;
162         }
163     }
164 
165     @Override
unregister()166     public void unregister() {
167         synchronized (lock) {
168             if (mxbeanLookup != null) {
169                 if (mxbeanLookup.removeReference(objectName, getResource()))
170                     objectName = null;
171             }
172         }
173     }
174     private final Object lock = new Object(); // for mxbeanLookup and objectName
175 
176     private MXBeanLookup mxbeanLookup;
177     private ObjectName objectName;
178 }
179