jdk/test/javax/management/namespace/NamespaceController.java
changeset 4159 9e3aae7675f1
parent 4158 0b4d21bc8b5c
parent 4156 acaa49a2768a
child 4160 bda0a85afcb7
equal deleted inserted replaced
4158:0b4d21bc8b5c 4159:9e3aae7675f1
     1 /*
       
     2  * Copyright 2008 Sun Microsystems, Inc.  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.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 import com.sun.jmx.namespace.ObjectNameRouter;
       
    27 import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR;
       
    28 
       
    29 import java.io.IOException;
       
    30 import java.util.Arrays;
       
    31 import java.util.Map;
       
    32 import java.util.Set;
       
    33 import java.util.TreeSet;
       
    34 import java.util.logging.Level;
       
    35 import java.util.logging.Logger;
       
    36 
       
    37 import javax.management.InstanceAlreadyExistsException;
       
    38 import javax.management.InstanceNotFoundException;
       
    39 import javax.management.JMX;
       
    40 import javax.management.ListenerNotFoundException;
       
    41 import javax.management.MBeanException;
       
    42 import javax.management.MBeanNotificationInfo;
       
    43 import javax.management.MBeanRegistration;
       
    44 import javax.management.MBeanRegistrationException;
       
    45 import javax.management.MBeanServer;
       
    46 import javax.management.MBeanServerConnection;
       
    47 import javax.management.MalformedObjectNameException;
       
    48 import javax.management.NotCompliantMBeanException;
       
    49 import javax.management.Notification;
       
    50 import javax.management.NotificationBroadcasterSupport;
       
    51 import javax.management.NotificationEmitter;
       
    52 import javax.management.NotificationFilter;
       
    53 import javax.management.NotificationListener;
       
    54 import javax.management.ObjectInstance;
       
    55 import javax.management.ObjectName;
       
    56 import javax.management.ReflectionException;
       
    57 import javax.management.namespace.JMXNamespace;
       
    58 import javax.management.namespace.JMXNamespaces;
       
    59 import javax.management.namespace.JMXRemoteNamespaceMBean;
       
    60 import javax.management.remote.JMXServiceURL;
       
    61 
       
    62 /**
       
    63  * The {@code NamespaceController} MBean makes it possible to easily
       
    64  * create mount points ({@linkplain JMXNamespace JMXNamespaces}) in an
       
    65  * {@code MBeanServer}.
       
    66  * There is at most one instance of NamespaceController in an
       
    67  * MBeanServer - which can be created using the {@link #createInstance
       
    68  * createInstance} method. The {@code NamespaceController} MBean will
       
    69  * make it possible to remotely create name spaces by mounting remote
       
    70  * MBeanServers into the MBeanServer in which it was registered.
       
    71  */
       
    72 // This API was originally in the draft of javax/management/namespaces
       
    73 // but we decided to retire it. Rather than removing all the associated
       
    74 // tests I have moved the API to the test hierarchy - so it is now used as
       
    75 // an additional (though somewhat complex) test case...
       
    76 //
       
    77 public class NamespaceController implements NamespaceControllerMBean,
       
    78         NotificationEmitter, MBeanRegistration {
       
    79 
       
    80     /**
       
    81      * A logger for this class.
       
    82      **/
       
    83     private static final Logger LOG =
       
    84             Logger.getLogger(NamespaceController.class.getName());
       
    85 
       
    86     private static long seqNumber=0;
       
    87 
       
    88     private final NotificationBroadcasterSupport broadcaster =
       
    89             new NotificationBroadcasterSupport();
       
    90 
       
    91     private volatile MBeanServer mbeanServer = null;
       
    92 
       
    93     private volatile ObjectName objectName = null;
       
    94 
       
    95     //was: NamespaceController.class.getPackage().getName()
       
    96     public static final String NAMESPACE_CONTROLLER_DOMAIN = "jmx.ns";
       
    97 
       
    98     /**
       
    99      * Creates a new NamespaceController.
       
   100      * Using {@link #createInstance} should be preferred.
       
   101      **/
       
   102     public NamespaceController() {
       
   103         this(null);
       
   104     }
       
   105 
       
   106     public NamespaceController(MBeanServer mbeanServer) {
       
   107         this.mbeanServer = mbeanServer;
       
   108     }
       
   109 
       
   110     /*
       
   111      * MBeanNotification support
       
   112      * You shouldn't update these methods
       
   113      */
       
   114     public final void addNotificationListener(NotificationListener listener,
       
   115             NotificationFilter filter, Object handback) {
       
   116         broadcaster.addNotificationListener(listener, filter, handback);
       
   117     }
       
   118 
       
   119     public MBeanNotificationInfo[] getNotificationInfo() {
       
   120         return new MBeanNotificationInfo[] {
       
   121         };
       
   122     }
       
   123 
       
   124     public final void removeNotificationListener(NotificationListener listener)
       
   125         throws ListenerNotFoundException {
       
   126         broadcaster.removeNotificationListener(listener);
       
   127     }
       
   128 
       
   129     public final void removeNotificationListener(NotificationListener listener,
       
   130             NotificationFilter filter, Object handback)
       
   131             throws ListenerNotFoundException {
       
   132         broadcaster.removeNotificationListener(listener, filter, handback);
       
   133     }
       
   134 
       
   135     public static synchronized long getNextSeqNumber() {
       
   136         return seqNumber++;
       
   137     }
       
   138 
       
   139     protected final void sendNotification(Notification n) {
       
   140         if (n.getSequenceNumber()<=0)
       
   141             n.setSequenceNumber(getNextSeqNumber());
       
   142         if (n.getSource()==null)
       
   143             n.setSource(objectName);
       
   144         broadcaster.sendNotification(n);
       
   145     }
       
   146 
       
   147     /**
       
   148      * The ObjectName with which this MBean was registered.
       
   149      * <p>Unless changed by subclasses, this is
       
   150      * {@code
       
   151      *  "javax.management.namespace:type="+this.getClass().getSimpleName()}.
       
   152      * @return this MBean's ObjectName, or null if this MBean was never
       
   153      *         registered.
       
   154      **/
       
   155     public final ObjectName getObjectName() {
       
   156         return objectName;
       
   157     }
       
   158 
       
   159     /**
       
   160      * The MBeanServer  served by this NamespaceController.
       
   161      * @return the MBeanServer  served by this NamespaceController.
       
   162      **/
       
   163     public final MBeanServer getMBeanServer() {
       
   164         return mbeanServer;
       
   165     }
       
   166 
       
   167     /**
       
   168      * Allows the MBean to perform any operations it needs before being
       
   169      * registered in the MBean server. If the name of the MBean is not
       
   170      * specified, the MBean can provide a name for its registration. If
       
   171      * any exception is raised, the MBean will not be registered in the
       
   172      * MBean server. Subclasses which override {@code preRegister}
       
   173      * must call {@code super.preRegister(name,server)};
       
   174      * @param server The MBean server in which the MBean will be registered.
       
   175      * @param name The object name of the MBean.
       
   176      *        The name must be either {@code null} - or equal to that
       
   177      *        described by {@link #getObjectName}.
       
   178      * @return The name under which the MBean is to be registered.
       
   179      *         This will be the name described by {@link #getObjectName}.
       
   180      * @throws MalformedObjectNameException if the supplied name does not
       
   181      *        meet expected requirements.
       
   182      */
       
   183     public ObjectName preRegister(MBeanServer server, ObjectName name)
       
   184         throws MalformedObjectNameException {
       
   185         objectName = name;
       
   186         final ObjectName single =
       
   187                 ObjectName.getInstance(NAMESPACE_CONTROLLER_DOMAIN+
       
   188                 ":type="+this.getClass().getSimpleName());
       
   189         if (name!=null && !single.equals(name))
       
   190             throw new MalformedObjectNameException(name.toString());
       
   191         if (mbeanServer == null) mbeanServer = server;
       
   192         return single;
       
   193     }
       
   194 
       
   195     /**
       
   196      * Allows the MBean to perform any operations needed after having
       
   197      * been registered in the MBean server or after the registration has
       
   198      * failed.
       
   199      * @param registrationDone Indicates whether or not the MBean has been
       
   200      * successfully registered in the MBean server. The value false means
       
   201      * that the registration has failed.
       
   202      */
       
   203     public void postRegister(Boolean registrationDone) {
       
   204         //TODO postRegister implementation;
       
   205     }
       
   206 
       
   207     /**
       
   208      * Allows the MBean to perform any operations it needs before being
       
   209      * unregistered by the MBean server.
       
   210      * @throws Exception This exception will be caught by the MBean server and
       
   211      * re-thrown as an MBeanRegistrationException.
       
   212      */
       
   213     public void preDeregister() throws Exception {
       
   214         //TODO preDeregister implementation;
       
   215     }
       
   216 
       
   217     /**
       
   218      * Allows the MBean to perform any operations needed after having been
       
   219      * unregistered in the MBean server.
       
   220      */
       
   221     public void postDeregister() {
       
   222         //TODO postDeregister implementation;
       
   223     }
       
   224 
       
   225     public String mount(JMXServiceURL url,
       
   226             String targetPath,
       
   227             Map<String,Object> optionsMap)
       
   228             throws IOException {
       
   229         return mount(url, targetPath, "", optionsMap);
       
   230     }
       
   231 
       
   232     // see NamespaceControllerMBean
       
   233     public String mount(JMXServiceURL url,
       
   234             String targetPath,
       
   235             String sourcePath,
       
   236             Map<String,Object> optionsMap)
       
   237             throws IOException {
       
   238 
       
   239         // TODO: handle description.
       
   240         final String dirName =
       
   241                 JMXNamespaces.normalizeNamespaceName(targetPath);
       
   242 
       
   243          try {
       
   244             final ObjectInstance moi =
       
   245                     JMXRemoteTargetNamespace.createNamespace(mbeanServer,
       
   246                     dirName,url,optionsMap,
       
   247                     JMXNamespaces.normalizeNamespaceName(sourcePath)
       
   248                     );
       
   249             final ObjectName nsMBean = moi.getObjectName();
       
   250             try {
       
   251                 mbeanServer.invoke(nsMBean, "connect", null,null);
       
   252             } catch (Throwable t) {
       
   253                 mbeanServer.unregisterMBean(nsMBean);
       
   254                 throw t;
       
   255             }
       
   256             return getMountPointID(nsMBean);
       
   257         } catch (InstanceAlreadyExistsException x) {
       
   258             throw new IllegalArgumentException(targetPath,x);
       
   259          } catch (IOException x) {
       
   260             throw x;
       
   261         } catch (Throwable x) {
       
   262             if (x instanceof Error) throw (Error)x;
       
   263             Throwable cause = x.getCause();
       
   264             if (cause instanceof IOException)
       
   265                 throw ((IOException)cause);
       
   266             if (cause == null) cause = x;
       
   267 
       
   268             final IOException io =
       
   269                     new IOException("connect failed: "+cause);
       
   270             io.initCause(cause);
       
   271             throw io;
       
   272         }
       
   273     }
       
   274 
       
   275     private String getMountPointID(ObjectName dirName) {
       
   276             return dirName.toString();
       
   277     }
       
   278 
       
   279     private ObjectName getHandlerName(String mountPointID) {
       
   280         try {
       
   281             final ObjectName tryit = ObjectName.getInstance(mountPointID);
       
   282             final ObjectName formatted =
       
   283                     JMXNamespaces.getNamespaceObjectName(tryit.getDomain());
       
   284             if (!formatted.equals(tryit))
       
   285                 throw new IllegalArgumentException(mountPointID+
       
   286                         ": invalid mountPointID");
       
   287             return formatted;
       
   288         } catch (MalformedObjectNameException x) {
       
   289             throw new IllegalArgumentException(mountPointID,x);
       
   290         }
       
   291     }
       
   292 
       
   293     public boolean unmount(String mountPointID)
       
   294         throws IOException {
       
   295         final ObjectName dirName = getHandlerName(mountPointID);
       
   296         if (!mbeanServer.isRegistered(dirName))
       
   297             throw new IllegalArgumentException(mountPointID+
       
   298                     ": no such name space");
       
   299         final JMXRemoteNamespaceMBean mbean =
       
   300                 JMX.newMBeanProxy(mbeanServer,dirName,
       
   301                     JMXRemoteNamespaceMBean.class);
       
   302         try {
       
   303             mbean.close();
       
   304         } catch (IOException io) {
       
   305             LOG.fine("Failed to close properly - ignoring exception: "+io);
       
   306             LOG.log(Level.FINEST,
       
   307                     "Failed to close properly - ignoring exception",io);
       
   308         } finally {
       
   309             try {
       
   310                 mbeanServer.unregisterMBean(dirName);
       
   311             } catch (InstanceNotFoundException x) {
       
   312                 throw new IllegalArgumentException(mountPointID+
       
   313                         ": no such name space", x);
       
   314             } catch (MBeanRegistrationException x) {
       
   315                 final IOException io =
       
   316                         new IOException(mountPointID +": failed to unmount");
       
   317                 io.initCause(x);
       
   318                 throw io;
       
   319             }
       
   320         }
       
   321         return true;
       
   322     }
       
   323 
       
   324     public boolean ismounted(String targetPath) {
       
   325         return mbeanServer.isRegistered(JMXNamespaces.getNamespaceObjectName(targetPath));
       
   326     }
       
   327 
       
   328     public ObjectName getHandlerNameFor(String targetPath) {
       
   329         return JMXNamespaces.getNamespaceObjectName(targetPath);
       
   330     }
       
   331 
       
   332     public String[] findNamespaces() {
       
   333         return findNamespaces(null,null,0);
       
   334     }
       
   335 
       
   336 
       
   337     private ObjectName getDirPattern(String from) {
       
   338         try {
       
   339             if (from == null)
       
   340                 return ObjectName.getInstance(ALL_NAMESPACES);
       
   341             final String namespace =
       
   342                   ObjectNameRouter.normalizeNamespacePath(from,false,true,false);
       
   343             if (namespace.equals(""))
       
   344                 return ObjectName.getInstance(ALL_NAMESPACES);
       
   345             if (JMXNamespaces.getNamespaceObjectName(namespace).isDomainPattern())
       
   346                 throw new IllegalArgumentException(from);
       
   347             return ObjectName.getInstance(namespace+NAMESPACE_SEPARATOR+ALL_NAMESPACES);
       
   348         } catch (MalformedObjectNameException x) {
       
   349             throw new IllegalArgumentException(from,x);
       
   350         }
       
   351     }
       
   352 
       
   353     public String[] findNamespaces(String from, String regex, int depth) {
       
   354         if (depth < 0) return new String[0];
       
   355         final Set<String> res = new TreeSet<String>();
       
   356         final ObjectName all = getDirPattern(from);
       
   357         Set<ObjectName> names = mbeanServer.queryNames(all,null);
       
   358         for (ObjectName dirName : names) {
       
   359             final String dir = dirName.getDomain();
       
   360             if (regex == null || dir.matches(regex))
       
   361                 res.add(dir);
       
   362             if (depth > 0)
       
   363                 res.addAll(Arrays.asList(findNamespaces(dir,regex,depth-1)));
       
   364         }
       
   365         return res.toArray(new String[res.size()]);
       
   366     }
       
   367 
       
   368     /**
       
   369      * Creates a {@link NamespaceController} MBean in the provided
       
   370      * {@link MBeanServerConnection}.
       
   371      * <p>The name of the MBean is that returned by {@link #preRegister}
       
   372      * as described by {@link #getObjectName}.
       
   373      * @throws IOException if an {@code IOException} is raised when invoking
       
   374      *         the provided connection.
       
   375      * @throws InstanceAlreadyExistsException if an MBean was already
       
   376      *         registered with the NamespaceController's name.
       
   377      * @throws MBeanRegistrationException if thrown by {@link
       
   378      * MBeanServerConnection#createMBean(java.lang.String,javax.management.ObjectName)
       
   379      * server.createMBean}
       
   380      * @throws MBeanException if thrown by {@link
       
   381      * MBeanServerConnection#createMBean(java.lang.String,javax.management.ObjectName)
       
   382      * server.createMBean}
       
   383      * @return the {@link ObjectInstance}, as returned by {@link
       
   384      * MBeanServerConnection#createMBean(java.lang.String,javax.management.ObjectName)
       
   385      * server.createMBean}
       
   386      **/
       
   387     public static ObjectInstance createInstance(MBeanServerConnection server)
       
   388         throws IOException, InstanceAlreadyExistsException,
       
   389             MBeanRegistrationException, MBeanException {
       
   390         try {
       
   391             final ObjectInstance instance =
       
   392                 server.createMBean(NamespaceController.class.getName(), null);
       
   393             return instance;
       
   394         } catch (NotCompliantMBeanException ex) {
       
   395             throw new RuntimeException("unexpected exception: " + ex, ex);
       
   396         } catch (ReflectionException ex) {
       
   397             throw new RuntimeException("unexpected exception: " + ex, ex);
       
   398         }
       
   399     }
       
   400 
       
   401     private final static String ALL_NAMESPACES=
       
   402             "*"+NAMESPACE_SEPARATOR+":"+
       
   403             JMXNamespace.TYPE_ASSIGNMENT;
       
   404 
       
   405 }