View Javadoc
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      */
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 }