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
26package com.sun.jmx.mbeanserver;
27
28import static com.sun.jmx.mbeanserver.Util.*;
29
30import java.util.Iterator;
31import java.util.Set;
32
33import javax.management.InstanceAlreadyExistsException;
34import javax.management.JMX;
35import javax.management.MBeanServer;
36import javax.management.NotCompliantMBeanException;
37import javax.management.ObjectName;
38
39/**
40 * Base class for MXBeans.
41 *
42 * @since 1.6
43 */
44public 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    */
64    public <T> MXBeanSupport(T resource, Class<T> mxbeanInterface)
65            throws NotCompliantMBeanException {
66        super(resource, mxbeanInterface);
67    }
68
69    @Override
70    MBeanIntrospector<ConvertingMethod> getMBeanIntrospector() {
71        return MXBeanIntrospector.getInstance();
72    }
73
74    @Override
75    Object getCookie() {
76        return mxbeanLookup;
77    }
78
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     */
118    private static Set<Class<?>> transitiveInterfaces(Class<?> c) {
119        Set<Class<?>> set = newSet();
120        transitiveInterfaces(c, set);
121        return set;
122    }
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
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
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