/*
* Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
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.
*
* 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:
*
*
Each file must be separated from the one that follows it by a comma (,).
*
archivelist must be enclosed in double quote marks.
*
* 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=argumentTypeVALUE=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