diff -r 836adbf7a2cd -r 3317bb8137f4 jdk/src/java.management/share/classes/javax/management/loading/MLet.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.management/share/classes/javax/management/loading/MLet.java Sun Aug 17 15:54:13 2014 +0100 @@ -0,0 +1,1338 @@ +/* + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.management.loading; + +// Java import +import com.sun.jmx.defaults.JmxProperties; + +import com.sun.jmx.defaults.ServiceName; + +import com.sun.jmx.remote.util.EnvHelp; + +import java.io.Externalizable; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInput; +import java.io.ObjectInputStream; +import java.io.ObjectOutput; +import java.lang.reflect.Constructor; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLStreamHandlerFactory; +import java.nio.file.Files; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.logging.Level; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; + +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanRegistration; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServer; +import javax.management.NotCompliantMBeanException; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.ReflectionException; + +import static com.sun.jmx.defaults.JmxProperties.MLET_LIB_DIR; +import static com.sun.jmx.defaults.JmxProperties.MLET_LOGGER; +import com.sun.jmx.defaults.ServiceName; +import javax.management.ServiceNotFoundException; + +/** + * Allows you to instantiate and register one or several MBeans in the MBean server + * coming from a remote URL. M-let is a shortcut for management applet. The m-let service does this + * by loading an m-let text file, which specifies information on the MBeans to be obtained. + * The information on each MBean is specified in a single instance of a tag, called the MLET tag. + * The location of the m-let text file is specified by a URL. + *

+ * The MLET tag has the following syntax: + *

+ * <MLET
+ * CODE = class | OBJECT = serfile
+ * ARCHIVE = "archiveList"
+ * [CODEBASE = codebaseURL]
+ * [NAME = mbeanname]
+ * [VERSION = version]
+ * >
+ * [arglist]
+ * </MLET> + *

+ * where: + *

+ *
CODE = class
+ *
+ * This attribute specifies the full Java class name, including package name, of the MBean to be obtained. + * The compiled .class file of the MBean must be contained in one of the .jar files specified by the ARCHIVE + * attribute. Either CODE or OBJECT must be present. + *
+ *
OBJECT = serfile
+ *
+ * This attribute specifies the .ser file that contains a serialized representation of the MBean to be obtained. + * This file must be contained in one of the .jar files specified by the ARCHIVE attribute. If the .jar file contains a directory hierarchy, specify the path of the file within this hierarchy. Otherwise a match will not be found. Either CODE or OBJECT must be present. + *
+ *
ARCHIVE = "archiveList"
+ *
+ * This mandatory attribute specifies one or more .jar files + * containing MBeans or other resources used by + * the MBean to be obtained. One of the .jar files must contain the file specified by the CODE or OBJECT attribute. + * If archivelist contains more than one file: + * + * All .jar files in archivelist must be stored in the directory specified by the code base URL. + *
+ *
CODEBASE = codebaseURL
+ *
+ * This optional attribute specifies the code base URL of the MBean to be obtained. It identifies the directory that contains + * the .jar files specified by the ARCHIVE attribute. Specify this attribute only if the .jar files are not in the same + * directory as the m-let text file. If this attribute is not specified, the base URL of the m-let text file is used. + *
+ *
NAME = mbeanname
+ *
+ * This optional attribute specifies the object name to be assigned to the + * MBean instance when the m-let service registers it. If + * mbeanname starts with the colon character (:), the domain + * part of the object name is the default domain of the MBean server, + * as returned by {@link javax.management.MBeanServer#getDefaultDomain()}. + *
+ *
VERSION = version
+ *
+ * This optional attribute specifies the version number of the MBean and + * associated .jar files to be obtained. This version number can + * be used to specify that the .jar files are loaded from the + * server to update those stored locally in the cache the next time the m-let + * text file is loaded. version must be a series of non-negative + * decimal integers each separated by a period from the one that precedes it. + *
+ *
arglist
+ *
+ * This optional attribute specifies a list of one or more parameters for the + * MBean to be instantiated. This list describes the parameters to be passed the MBean's constructor. + * Use the following syntax to specify each item in + * arglist: + *
+ *
<ARG TYPE=argumentType VALUE=value>
+ *
where: + *
    + *
  • argumentType is the type of the argument that will be passed as parameter to the MBean's constructor.
+ *
+ *
+ *

The arguments' type in the argument list should be a Java primitive type or a Java basic type + * (java.lang.Boolean, java.lang.Byte, java.lang.Short, java.lang.Long, java.lang.Integer, java.lang.Float, java.lang.Double, java.lang.String). + *

+ *
+ * + * When an m-let text file is loaded, an + * instance of each MBean specified in the file is created and registered. + *

+ * The m-let service extends the java.net.URLClassLoader and can be used to load remote classes + * and jar files in the VM of the agent. + *

Note - The MLet class loader uses the {@link javax.management.MBeanServerFactory#getClassLoaderRepository(javax.management.MBeanServer)} + * to load classes that could not be found in the loaded jar files. + * + * @since 1.5 + */ +public class MLet extends java.net.URLClassLoader + implements MLetMBean, MBeanRegistration, Externalizable { + + private static final long serialVersionUID = 3636148327800330130L; + + /* + * ------------------------------------------ + * PRIVATE VARIABLES + * ------------------------------------------ + */ + + /** + * The reference to the MBean server. + * @serial + */ + private MBeanServer server = null; + + + /** + * The list of instances of the MLetContent + * class found at the specified URL. + * @serial + */ + private List mletList = new ArrayList(); + + + /** + * The directory used for storing libraries locally before they are loaded. + */ + private String libraryDirectory; + + + /** + * The object name of the MLet Service. + * @serial + */ + private ObjectName mletObjectName = null; + + /** + * The URLs of the MLet Service. + * @serial + */ + private URL[] myUrls = null; + + /** + * What ClassLoaderRepository, if any, to use if this MLet + * doesn't find an asked-for class. + */ + private transient ClassLoaderRepository currentClr; + + /** + * True if we should consult the {@link ClassLoaderRepository} + * when we do not find a class ourselves. + */ + private transient boolean delegateToCLR; + + /** + * objects maps from primitive classes to primitive object classes. + */ + private Map> primitiveClasses = + new HashMap>(8) ; + { + primitiveClasses.put(Boolean.TYPE.toString(), Boolean.class); + primitiveClasses.put(Character.TYPE.toString(), Character.class); + primitiveClasses.put(Byte.TYPE.toString(), Byte.class); + primitiveClasses.put(Short.TYPE.toString(), Short.class); + primitiveClasses.put(Integer.TYPE.toString(), Integer.class); + primitiveClasses.put(Long.TYPE.toString(), Long.class); + primitiveClasses.put(Float.TYPE.toString(), Float.class); + primitiveClasses.put(Double.TYPE.toString(), Double.class); + + } + + + /* + * ------------------------------------------ + * CONSTRUCTORS + * ------------------------------------------ + */ + + /* + * The constructor stuff would be considerably simplified if our + * parent, URLClassLoader, specified that its one- and + * two-argument constructors were equivalent to its + * three-argument constructor with trailing null arguments. But + * it doesn't, which prevents us from having all the constructors + * but one call this(...args...). + */ + + /** + * Constructs a new MLet using the default delegation parent ClassLoader. + */ + public MLet() { + this(new URL[0]); + } + + /** + * Constructs a new MLet for the specified URLs using the default + * delegation parent ClassLoader. The URLs will be searched in + * the order specified for classes and resources after first + * searching in the parent class loader. + * + * @param urls The URLs from which to load classes and resources. + * + */ + public MLet(URL[] urls) { + this(urls, true); + } + + /** + * Constructs a new MLet for the given URLs. The URLs will be + * searched in the order specified for classes and resources + * after first searching in the specified parent class loader. + * The parent argument will be used as the parent class loader + * for delegation. + * + * @param urls The URLs from which to load classes and resources. + * @param parent The parent class loader for delegation. + * + */ + public MLet(URL[] urls, ClassLoader parent) { + this(urls, parent, true); + } + + /** + * Constructs a new MLet for the specified URLs, parent class + * loader, and URLStreamHandlerFactory. The parent argument will + * be used as the parent class loader for delegation. The factory + * argument will be used as the stream handler factory to obtain + * protocol handlers when creating new URLs. + * + * @param urls The URLs from which to load classes and resources. + * @param parent The parent class loader for delegation. + * @param factory The URLStreamHandlerFactory to use when creating URLs. + * + */ + public MLet(URL[] urls, + ClassLoader parent, + URLStreamHandlerFactory factory) { + this(urls, parent, factory, true); + } + + /** + * Constructs a new MLet for the specified URLs using the default + * delegation parent ClassLoader. The URLs will be searched in + * the order specified for classes and resources after first + * searching in the parent class loader. + * + * @param urls The URLs from which to load classes and resources. + * @param delegateToCLR True if, when a class is not found in + * either the parent ClassLoader or the URLs, the MLet should delegate + * to its containing MBeanServer's {@link ClassLoaderRepository}. + * + */ + public MLet(URL[] urls, boolean delegateToCLR) { + super(urls); + init(delegateToCLR); + } + + /** + * Constructs a new MLet for the given URLs. The URLs will be + * searched in the order specified for classes and resources + * after first searching in the specified parent class loader. + * The parent argument will be used as the parent class loader + * for delegation. + * + * @param urls The URLs from which to load classes and resources. + * @param parent The parent class loader for delegation. + * @param delegateToCLR True if, when a class is not found in + * either the parent ClassLoader or the URLs, the MLet should delegate + * to its containing MBeanServer's {@link ClassLoaderRepository}. + * + */ + public MLet(URL[] urls, ClassLoader parent, boolean delegateToCLR) { + super(urls, parent); + init(delegateToCLR); + } + + /** + * Constructs a new MLet for the specified URLs, parent class + * loader, and URLStreamHandlerFactory. The parent argument will + * be used as the parent class loader for delegation. The factory + * argument will be used as the stream handler factory to obtain + * protocol handlers when creating new URLs. + * + * @param urls The URLs from which to load classes and resources. + * @param parent The parent class loader for delegation. + * @param factory The URLStreamHandlerFactory to use when creating URLs. + * @param delegateToCLR True if, when a class is not found in + * either the parent ClassLoader or the URLs, the MLet should delegate + * to its containing MBeanServer's {@link ClassLoaderRepository}. + * + */ + public MLet(URL[] urls, + ClassLoader parent, + URLStreamHandlerFactory factory, + boolean delegateToCLR) { + super(urls, parent, factory); + init(delegateToCLR); + } + + private void init(boolean delegateToCLR) { + this.delegateToCLR = delegateToCLR; + + try { + libraryDirectory = System.getProperty(MLET_LIB_DIR); + if (libraryDirectory == null) + libraryDirectory = getTmpDir(); + } catch (SecurityException e) { + // OK : We don't do AccessController.doPrivileged, but we don't + // stop the user from creating an MLet just because they + // can't read the MLET_LIB_DIR or java.io.tmpdir properties + // either. + } + } + + + /* + * ------------------------------------------ + * PUBLIC METHODS + * ------------------------------------------ + */ + + + /** + * Appends the specified URL to the list of URLs to search for classes and + * resources. + */ + public void addURL(URL url) { + if (!Arrays.asList(getURLs()).contains(url)) + super.addURL(url); + } + + /** + * Appends the specified URL to the list of URLs to search for classes and + * resources. + * @exception ServiceNotFoundException The specified URL is malformed. + */ + public void addURL(String url) throws ServiceNotFoundException { + try { + URL ur = new URL(url); + if (!Arrays.asList(getURLs()).contains(ur)) + super.addURL(ur); + } catch (MalformedURLException e) { + if (MLET_LOGGER.isLoggable(Level.FINEST)) { + MLET_LOGGER.logp(Level.FINEST, MLet.class.getName(), + "addUrl", "Malformed URL: " + url, e); + } + throw new + ServiceNotFoundException("The specified URL is malformed"); + } + } + + /** Returns the search path of URLs for loading classes and resources. + * This includes the original list of URLs specified to the constructor, + * along with any URLs subsequently appended by the addURL() method. + */ + public URL[] getURLs() { + return super.getURLs(); + } + + /** + * Loads a text file containing MLET tags that define the MBeans to + * be added to the MBean server. The location of the text file is specified by + * a URL. The MBeans specified in the MLET file will be instantiated and + * registered in the MBean server. + * + * @param url The URL of the text file to be loaded as URL object. + * + * @return A set containing one entry per MLET tag in the m-let text file loaded. + * Each entry specifies either the ObjectInstance for the created MBean, or a throwable object + * (that is, an error or an exception) if the MBean could not be created. + * + * @exception ServiceNotFoundException One of the following errors has occurred: The m-let text file does + * not contain an MLET tag, the m-let text file is not found, a mandatory + * attribute of the MLET tag is not specified, the value of url is + * null. + * @exception IllegalStateException MLet MBean is not registered with an MBeanServer. + */ + public Set getMBeansFromURL(URL url) + throws ServiceNotFoundException { + if (url == null) { + throw new ServiceNotFoundException("The specified URL is null"); + } + return getMBeansFromURL(url.toString()); + } + + /** + * Loads a text file containing MLET tags that define the MBeans to + * be added to the MBean server. The location of the text file is specified by + * a URL. The MBeans specified in the MLET file will be instantiated and + * registered in the MBean server. + * + * @param url The URL of the text file to be loaded as String object. + * + * @return A set containing one entry per MLET tag in the m-let + * text file loaded. Each entry specifies either the + * ObjectInstance for the created MBean, or a throwable object + * (that is, an error or an exception) if the MBean could not be + * created. + * + * @exception ServiceNotFoundException One of the following + * errors has occurred: The m-let text file does not contain an + * MLET tag, the m-let text file is not found, a mandatory + * attribute of the MLET tag is not specified, the url is + * malformed. + * @exception IllegalStateException MLet MBean is not registered + * with an MBeanServer. + * + */ + public Set getMBeansFromURL(String url) + throws ServiceNotFoundException { + + String mth = "getMBeansFromURL"; + + if (server == null) { + throw new IllegalStateException("This MLet MBean is not " + + "registered with an MBeanServer."); + } + // Parse arguments + if (url == null) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), + mth, "URL is null"); + throw new ServiceNotFoundException("The specified URL is null"); + } else { + url = url.replace(File.separatorChar,'/'); + } + if (MLET_LOGGER.isLoggable(Level.FINER)) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), + mth, ""); + } + + // Parse URL + try { + MLetParser parser = new MLetParser(); + mletList = parser.parseURL(url); + } catch (Exception e) { + final String msg = + "Problems while parsing URL [" + url + + "], got exception [" + e.toString() + "]"; + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, msg); + throw EnvHelp.initCause(new ServiceNotFoundException(msg), e); + } + + // Check that the list of MLets is not empty + if (mletList.size() == 0) { + final String msg = + "File " + url + " not found or MLET tag not defined in file"; + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, msg); + throw new ServiceNotFoundException(msg); + } + + // Walk through the list of MLets + Set mbeans = new HashSet(); + for (MLetContent elmt : mletList) { + // Initialize local variables + String code = elmt.getCode(); + if (code != null) { + if (code.endsWith(".class")) { + code = code.substring(0, code.length() - 6); + } + } + String name = elmt.getName(); + URL codebase = elmt.getCodeBase(); + String version = elmt.getVersion(); + String serName = elmt.getSerializedObject(); + String jarFiles = elmt.getJarFiles(); + URL documentBase = elmt.getDocumentBase(); + + // Display debug information + if (MLET_LOGGER.isLoggable(Level.FINER)) { + final StringBuilder strb = new StringBuilder() + .append("\n\tMLET TAG = ").append(elmt.getAttributes()) + .append("\n\tCODEBASE = ").append(codebase) + .append("\n\tARCHIVE = ").append(jarFiles) + .append("\n\tCODE = ").append(code) + .append("\n\tOBJECT = ").append(serName) + .append("\n\tNAME = ").append(name) + .append("\n\tVERSION = ").append(version) + .append("\n\tDOCUMENT URL = ").append(documentBase); + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), + mth, strb.toString()); + } + + // Load classes from JAR files + StringTokenizer st = new StringTokenizer(jarFiles, ",", false); + while (st.hasMoreTokens()) { + String tok = st.nextToken().trim(); + if (MLET_LOGGER.isLoggable(Level.FINER)) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, + "Load archive for codebase <" + codebase + + ">, file <" + tok + ">"); + } + // Check which is the codebase to be used for loading the jar file. + // If we are using the base MLet implementation then it will be + // always the remote server but if the service has been extended in + // order to support caching and versioning then this method will + // return the appropriate one. + // + try { + codebase = check(version, codebase, tok, elmt); + } catch (Exception ex) { + MLET_LOGGER.logp(Level.FINEST, MLet.class.getName(), + mth, "Got unexpected exception", ex); + mbeans.add(ex); + continue; + } + + // Appends the specified JAR file URL to the list of + // URLs to search for classes and resources. + try { + if (!Arrays.asList(getURLs()) + .contains(new URL(codebase.toString() + tok))) { + addURL(codebase + tok); + } + } catch (MalformedURLException me) { + // OK : Ignore jar file if its name provokes the + // URL to be an invalid one. + } + + } + // Instantiate the class specified in the + // CODE or OBJECT section of the MLet tag + // + Object o; + ObjectInstance objInst; + + if (code != null && serName != null) { + final String msg = + "CODE and OBJECT parameters cannot be specified at the " + + "same time in tag MLET"; + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, msg); + mbeans.add(new Error(msg)); + continue; + } + if (code == null && serName == null) { + final String msg = + "Either CODE or OBJECT parameter must be specified in " + + "tag MLET"; + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, msg); + mbeans.add(new Error(msg)); + continue; + } + try { + if (code != null) { + + List signat = elmt.getParameterTypes(); + List stringPars = elmt.getParameterValues(); + List objectPars = new ArrayList(); + + for (int i = 0; i < signat.size(); i++) { + objectPars.add(constructParameter(stringPars.get(i), + signat.get(i))); + } + if (signat.isEmpty()) { + if (name == null) { + objInst = server.createMBean(code, null, + mletObjectName); + } else { + objInst = server.createMBean(code, + new ObjectName(name), + mletObjectName); + } + } else { + Object[] parms = objectPars.toArray(); + String[] signature = new String[signat.size()]; + signat.toArray(signature); + if (MLET_LOGGER.isLoggable(Level.FINEST)) { + final StringBuilder strb = new StringBuilder(); + for (int i = 0; i < signature.length; i++) { + strb.append("\n\tSignature = ") + .append(signature[i]) + .append("\t\nParams = ") + .append(parms[i]); + } + MLET_LOGGER.logp(Level.FINEST, + MLet.class.getName(), + mth, strb.toString()); + } + if (name == null) { + objInst = + server.createMBean(code, null, mletObjectName, + parms, signature); + } else { + objInst = + server.createMBean(code, new ObjectName(name), + mletObjectName, parms, + signature); + } + } + } else { + o = loadSerializedObject(codebase,serName); + if (name == null) { + server.registerMBean(o, null); + } else { + server.registerMBean(o, new ObjectName(name)); + } + objInst = new ObjectInstance(name, o.getClass().getName()); + } + } catch (ReflectionException ex) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, + "ReflectionException", ex); + mbeans.add(ex); + continue; + } catch (InstanceAlreadyExistsException ex) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, + "InstanceAlreadyExistsException", ex); + mbeans.add(ex); + continue; + } catch (MBeanRegistrationException ex) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, + "MBeanRegistrationException", ex); + mbeans.add(ex); + continue; + } catch (MBeanException ex) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, + "MBeanException", ex); + mbeans.add(ex); + continue; + } catch (NotCompliantMBeanException ex) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, + "NotCompliantMBeanException", ex); + mbeans.add(ex); + continue; + } catch (InstanceNotFoundException ex) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, + "InstanceNotFoundException", ex); + mbeans.add(ex); + continue; + } catch (IOException ex) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, + "IOException", ex); + mbeans.add(ex); + continue; + } catch (SecurityException ex) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, + "SecurityException", ex); + mbeans.add(ex); + continue; + } catch (Exception ex) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, + "Exception", ex); + mbeans.add(ex); + continue; + } catch (Error ex) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, + "Error", ex); + mbeans.add(ex); + continue; + } + mbeans.add(objInst); + } + return mbeans; + } + + /** + * Gets the current directory used by the library loader for + * storing native libraries before they are loaded into memory. + * + * @return The current directory used by the library loader. + * + * @see #setLibraryDirectory + * + * @throws UnsupportedOperationException if this implementation + * does not support storing native libraries in this way. + */ + public synchronized String getLibraryDirectory() { + return libraryDirectory; + } + + /** + * Sets the directory used by the library loader for storing + * native libraries before they are loaded into memory. + * + * @param libdir The directory used by the library loader. + * + * @see #getLibraryDirectory + * + * @throws UnsupportedOperationException if this implementation + * does not support storing native libraries in this way. + */ + public synchronized void setLibraryDirectory(String libdir) { + libraryDirectory = libdir; + } + + /** + * Allows the m-let to perform any operations it needs before + * being registered in the MBean server. If the ObjectName is + * null, the m-let provides a default name for its registration + * <defaultDomain>:type=MLet + * + * @param server The MBean server in which the m-let will be registered. + * @param name The object name of the m-let. + * + * @return The name of the m-let registered. + * + * @exception java.lang.Exception This exception should be caught by the MBean server and re-thrown + *as an MBeanRegistrationException. + */ + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception { + + // Initialize local pointer to the MBean server + setMBeanServer(server); + + // If no name is specified return a default name for the MLet + if (name == null) { + name = new ObjectName(server.getDefaultDomain() + ":" + ServiceName.MLET); + } + + this.mletObjectName = name; + return this.mletObjectName; + } + + /** + * Allows the m-let to perform any operations needed after having been + * registered in the MBean server or after the registration has failed. + * + * @param registrationDone Indicates whether or not the m-let has + * been successfully registered in the MBean server. The value + * false means that either the registration phase has failed. + * + */ + public void postRegister (Boolean registrationDone) { + } + + /** + * Allows the m-let to perform any operations it needs before being unregistered + * by the MBean server. + * + * @exception java.lang.Exception This exception should be caught + * by the MBean server and re-thrown as an + * MBeanRegistrationException. + */ + public void preDeregister() throws java.lang.Exception { + } + + + /** + * Allows the m-let to perform any operations needed after having been + * unregistered in the MBean server. + */ + public void postDeregister() { + } + + /** + *

Save this MLet's contents to the given {@link ObjectOutput}. + * Not all implementations support this method. Those that do not + * throw {@link UnsupportedOperationException}. A subclass may + * override this method to support it or to change the format of + * the written data.

+ * + *

The format of the written data is not specified, but if + * an implementation supports {@link #writeExternal} it must + * also support {@link #readExternal} in such a way that what is + * written by the former can be read by the latter.

+ * + * @param out The object output stream to write to. + * + * @exception IOException If a problem occurred while writing. + * @exception UnsupportedOperationException If this + * implementation does not support this operation. + */ + public void writeExternal(ObjectOutput out) + throws IOException, UnsupportedOperationException { + throw new UnsupportedOperationException("MLet.writeExternal"); + } + + /** + *

Restore this MLet's contents from the given {@link ObjectInput}. + * Not all implementations support this method. Those that do not + * throw {@link UnsupportedOperationException}. A subclass may + * override this method to support it or to change the format of + * the read data.

+ * + *

The format of the read data is not specified, but if an + * implementation supports {@link #readExternal} it must also + * support {@link #writeExternal} in such a way that what is + * written by the latter can be read by the former.

+ * + * @param in The object input stream to read from. + * + * @exception IOException if a problem occurred while reading. + * @exception ClassNotFoundException if the class for the object + * being restored cannot be found. + * @exception UnsupportedOperationException if this + * implementation does not support this operation. + */ + public void readExternal(ObjectInput in) + throws IOException, ClassNotFoundException, + UnsupportedOperationException { + throw new UnsupportedOperationException("MLet.readExternal"); + } + + /* + * ------------------------------------------ + * PACKAGE METHODS + * ------------------------------------------ + */ + + /** + *

Load a class, using the given {@link ClassLoaderRepository} if + * the class is not found in this MLet's URLs. The given + * ClassLoaderRepository can be null, in which case a {@link + * ClassNotFoundException} occurs immediately if the class is not + * found in this MLet's URLs.

+ * + * @param name The name of the class we want to load. + * @param clr The ClassLoaderRepository that will be used to search + * for the given class, if it is not found in this + * ClassLoader. May be null. + * @return The resulting Class object. + * @exception ClassNotFoundException The specified class could not be + * found in this ClassLoader nor in the given + * ClassLoaderRepository. + * + */ + public synchronized Class loadClass(String name, + ClassLoaderRepository clr) + throws ClassNotFoundException { + final ClassLoaderRepository before=currentClr; + try { + currentClr = clr; + return loadClass(name); + } finally { + currentClr = before; + } + } + + /* + * ------------------------------------------ + * PROTECTED METHODS + * ------------------------------------------ + */ + + /** + * This is the main method for class loaders that is being redefined. + * + * @param name The name of the class. + * + * @return The resulting Class object. + * + * @exception ClassNotFoundException The specified class could not be + * found. + */ + protected Class findClass(String name) throws ClassNotFoundException { + /* currentClr is context sensitive - used to avoid recursion + in the class loader repository. (This is no longer + necessary with the new CLR semantics but is kept for + compatibility with code that might have called the + two-parameter loadClass explicitly.) */ + return findClass(name, currentClr); + } + + /** + * Called by {@link MLet#findClass(java.lang.String)}. + * + * @param name The name of the class that we want to load/find. + * @param clr The ClassLoaderRepository that can be used to search + * for the given class. This parameter is + * null when called from within the + * {@link javax.management.MBeanServerFactory#getClassLoaderRepository(javax.management.MBeanServer) Class Loader Repository}. + * @exception ClassNotFoundException The specified class could not be + * found. + * + **/ + Class findClass(String name, ClassLoaderRepository clr) + throws ClassNotFoundException { + Class c = null; + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), "findClass", name); + // Try looking in the JAR: + try { + c = super.findClass(name); + if (MLET_LOGGER.isLoggable(Level.FINER)) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), + "findClass", + "Class " + name + " loaded through MLet classloader"); + } + } catch (ClassNotFoundException e) { + // Drop through + if (MLET_LOGGER.isLoggable(Level.FINEST)) { + MLET_LOGGER.logp(Level.FINEST, MLet.class.getName(), + "findClass", + "Class " + name + " not found locally"); + } + } + // if we are not called from the ClassLoaderRepository + if (c == null && delegateToCLR && clr != null) { + // Try the classloader repository: + // + try { + if (MLET_LOGGER.isLoggable(Level.FINEST)) { + MLET_LOGGER.logp(Level.FINEST, MLet.class.getName(), + "findClass", + "Class " + name + " : looking in CLR"); + } + c = clr.loadClassBefore(this, name); + // The loadClassBefore method never returns null. + // If the class is not found we get an exception. + if (MLET_LOGGER.isLoggable(Level.FINER)) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), + "findClass", + "Class " + name + " loaded through " + + "the default classloader repository"); + } + } catch (ClassNotFoundException e) { + // Drop through + if (MLET_LOGGER.isLoggable(Level.FINEST)) { + MLET_LOGGER.logp(Level.FINEST, MLet.class.getName(), + "findClass", + "Class " + name + " not found in CLR"); + } + } + } + if (c == null) { + MLET_LOGGER.logp(Level.FINEST, MLet.class.getName(), + "findClass", "Failed to load class " + name); + throw new ClassNotFoundException(name); + } + return c; + } + + /** + * Returns the absolute path name of a native library. The VM + * invokes this method to locate the native libraries that belong + * to classes loaded with this class loader. Libraries are + * searched in the JAR files using first just the native library + * name and if not found the native library name together with + * the architecture-specific path name + * (OSName/OSArch/OSVersion/lib/nativelibname), i.e. + *

+ * the library stat on Solaris SPARC 5.7 will be searched in the JAR file as: + *

    + *
  1. libstat.so + *
  2. SunOS/sparc/5.7/lib/libstat.so + *
+ * the library stat on Windows NT 4.0 will be searched in the JAR file as: + *
    + *
  1. stat.dll + *
  2. WindowsNT/x86/4.0/lib/stat.dll + *
+ * + *

More specifically, let {@code nativelibname} be the result of + * {@link System#mapLibraryName(java.lang.String) + * System.mapLibraryName}{@code (libname)}. Then the following names are + * searched in the JAR files, in order:
+ * {@code nativelibname}
+ * {@code ///lib/}{@code nativelibname}
+ * where {@code } means {@code System.getProperty(X)} with any + * spaces in the result removed, and {@code /} stands for the + * file separator character ({@link File#separator}). + *

+ * If this method returns null, i.e. the libraries + * were not found in any of the JAR files loaded with this class + * loader, the VM searches the library along the path specified + * as the java.library.path property. + * + * @param libname The library name. + * + * @return The absolute path of the native library. + */ + protected String findLibrary(String libname) { + + String abs_path; + String mth = "findLibrary"; + + // Get the platform-specific string representing a native library. + // + String nativelibname = System.mapLibraryName(libname); + + // + // See if the native library is accessible as a resource through the JAR file. + // + if (MLET_LOGGER.isLoggable(Level.FINER)) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, + "Search " + libname + " in all JAR files"); + } + + // First try to locate the library in the JAR file using only + // the native library name. e.g. if user requested a load + // for "foo" on Solaris SPARC 5.7 we try to load "libfoo.so" + // from the JAR file. + // + if (MLET_LOGGER.isLoggable(Level.FINER)) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, + "loadLibraryAsResource(" + nativelibname + ")"); + } + abs_path = loadLibraryAsResource(nativelibname); + if (abs_path != null) { + if (MLET_LOGGER.isLoggable(Level.FINER)) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, + nativelibname + " loaded, absolute path = " + abs_path); + } + return abs_path; + } + + // Next try to locate it using the native library name and + // the architecture-specific path name. e.g. if user + // requested a load for "foo" on Solaris SPARC 5.7 we try to + // load "SunOS/sparc/5.7/lib/libfoo.so" from the JAR file. + // + nativelibname = removeSpace(System.getProperty("os.name")) + File.separator + + removeSpace(System.getProperty("os.arch")) + File.separator + + removeSpace(System.getProperty("os.version")) + File.separator + + "lib" + File.separator + nativelibname; + if (MLET_LOGGER.isLoggable(Level.FINER)) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, + "loadLibraryAsResource(" + nativelibname + ")"); + } + + abs_path = loadLibraryAsResource(nativelibname); + if (abs_path != null) { + if (MLET_LOGGER.isLoggable(Level.FINER)) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, + nativelibname + " loaded, absolute path = " + abs_path); + } + return abs_path; + } + + // + // All paths exhausted, library not found in JAR file. + // + + if (MLET_LOGGER.isLoggable(Level.FINER)) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, + libname + " not found in any JAR file"); + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), mth, + "Search " + libname + " along the path " + + "specified as the java.library.path property"); + } + + // Let the VM search the library along the path + // specified as the java.library.path property. + // + return null; + } + + + /* + * ------------------------------------------ + * PRIVATE METHODS + * ------------------------------------------ + */ + + private String getTmpDir() { + // JDK 1.4 + String tmpDir = System.getProperty("java.io.tmpdir"); + if (tmpDir != null) return tmpDir; + + // JDK < 1.4 + File tmpFile = null; + try { + // Try to guess the system temporary dir... + tmpFile = File.createTempFile("tmp","jmx"); + if (tmpFile == null) return null; + final File tmpDirFile = tmpFile.getParentFile(); + if (tmpDirFile == null) return null; + return tmpDirFile.getAbsolutePath(); + } catch (Exception x) { + MLET_LOGGER.logp(Level.FINEST, MLet.class.getName(), + "getTmpDir", "Failed to determine system temporary dir"); + return null; + } finally { + // Cleanup ... + if (tmpFile!=null) { + try { + boolean deleted = tmpFile.delete(); + if (!deleted) { + MLET_LOGGER.logp(Level.FINEST, MLet.class.getName(), + "getTmpDir", "Failed to delete temp file"); + } + } catch (Exception x) { + MLET_LOGGER.logp(Level.FINEST, MLet.class.getName(), + "getTmpDir", "Failed to delete temporary file", x); + } + } + } + } + + /** + * Search the specified native library in any of the JAR files + * loaded by this classloader. If the library is found copy it + * into the library directory and return the absolute path. If + * the library is not found then return null. + */ + private synchronized String loadLibraryAsResource(String libname) { + try { + InputStream is = getResourceAsStream( + libname.replace(File.separatorChar,'/')); + if (is != null) { + try { + File directory = new File(libraryDirectory); + directory.mkdirs(); + File file = Files.createTempFile(directory.toPath(), + libname + ".", null) + .toFile(); + file.deleteOnExit(); + FileOutputStream fileOutput = new FileOutputStream(file); + try { + byte[] buf = new byte[4096]; + int n; + while ((n = is.read(buf)) >= 0) { + fileOutput.write(buf, 0, n); + } + } finally { + fileOutput.close(); + } + if (file.exists()) { + return file.getAbsolutePath(); + } + } finally { + is.close(); + } + } + } catch (Exception e) { + MLET_LOGGER.logp(Level.FINEST, MLet.class.getName(), + "loadLibraryAsResource", + "Failed to load library : " + libname, e); + return null; + } + return null; + } + + /** + * Removes any white space from a string. This is used to + * convert strings such as "Windows NT" to "WindowsNT". + */ + private static String removeSpace(String s) { + return s.trim().replace(" ", ""); + } + + /** + *

This method is to be overridden when extending this service to + * support caching and versioning. It is called from {@link + * #getMBeansFromURL getMBeansFromURL} when the version, + * codebase, and jarfile have been extracted from the MLet file, + * and can be used to verify that it is all right to load the + * given MBean, or to replace the given URL with a different one.

+ * + *

The default implementation of this method returns + * codebase unchanged.

+ * + * @param version The version number of the .jar + * file stored locally. + * @param codebase The base URL of the remote .jar file. + * @param jarfile The name of the .jar file to be loaded. + * @param mlet The MLetContent instance that + * represents the MLET tag. + * + * @return the codebase to use for the loaded MBean. The returned + * value should not be null. + * + * @exception Exception if the MBean is not to be loaded for some + * reason. The exception will be added to the set returned by + * {@link #getMBeansFromURL getMBeansFromURL}. + * + */ + protected URL check(String version, URL codebase, String jarfile, + MLetContent mlet) + throws Exception { + return codebase; + } + + /** + * Loads the serialized object specified by the OBJECT + * attribute of the MLET tag. + * + * @param codebase The codebase. + * @param filename The name of the file containing the serialized object. + * @return The serialized object. + * @exception ClassNotFoundException The specified serialized + * object could not be found. + * @exception IOException An I/O error occurred while loading + * serialized object. + */ + private Object loadSerializedObject(URL codebase, String filename) + throws IOException, ClassNotFoundException { + if (filename != null) { + filename = filename.replace(File.separatorChar,'/'); + } + if (MLET_LOGGER.isLoggable(Level.FINER)) { + MLET_LOGGER.logp(Level.FINER, MLet.class.getName(), + "loadSerializedObject", codebase.toString() + filename); + } + InputStream is = getResourceAsStream(filename); + if (is != null) { + try { + ObjectInputStream ois = new MLetObjectInputStream(is, this); + Object serObject = ois.readObject(); + ois.close(); + return serObject; + } catch (IOException e) { + if (MLET_LOGGER.isLoggable(Level.FINEST)) { + MLET_LOGGER.logp(Level.FINEST, MLet.class.getName(), + "loadSerializedObject", + "Exception while deserializing " + filename, e); + } + throw e; + } catch (ClassNotFoundException e) { + if (MLET_LOGGER.isLoggable(Level.FINEST)) { + MLET_LOGGER.logp(Level.FINEST, MLet.class.getName(), + "loadSerializedObject", + "Exception while deserializing " + filename, e); + } + throw e; + } + } else { + if (MLET_LOGGER.isLoggable(Level.FINEST)) { + MLET_LOGGER.logp(Level.FINEST, MLet.class.getName(), + "loadSerializedObject", "Error: File " + filename + + " containing serialized object not found"); + } + throw new Error("File " + filename + " containing serialized object not found"); + } + } + + /** + * Converts the String value of the constructor's parameter to + * a basic Java object with the type of the parameter. + */ + private Object constructParameter(String param, String type) { + // check if it is a primitive type + Class c = primitiveClasses.get(type); + if (c != null) { + try { + Constructor cons = + c.getConstructor(String.class); + Object[] oo = new Object[1]; + oo[0]=param; + return(cons.newInstance(oo)); + + } catch (Exception e) { + MLET_LOGGER.logp(Level.FINEST, MLet.class.getName(), + "constructParameter", "Got unexpected exception", e); + } + } + if (type.compareTo("java.lang.Boolean") == 0) + return Boolean.valueOf(param); + if (type.compareTo("java.lang.Byte") == 0) + return Byte.valueOf(param); + if (type.compareTo("java.lang.Short") == 0) + return Short.valueOf(param); + if (type.compareTo("java.lang.Long") == 0) + return Long.valueOf(param); + if (type.compareTo("java.lang.Integer") == 0) + return param; + if (type.compareTo("java.lang.Float") == 0) + return new Float(param); + if (type.compareTo("java.lang.Double") == 0) + return new Double(param); + if (type.compareTo("java.lang.String") == 0) + return param; + + return param; + } + + private synchronized void setMBeanServer(final MBeanServer server) { + this.server = server; + PrivilegedAction act = + new PrivilegedAction() { + public ClassLoaderRepository run() { + return server.getClassLoaderRepository(); + } + }; + currentClr = AccessController.doPrivileged(act); + } + +}