001: /* JFox, the OpenSource J2EE Application Server
002: *
003: * Copyright (C) 2002 huihoo.org
004: * Distributable under GNU LGPL license
005: * See the GNU Lesser General Public License for more details.
006: */
007:
008: package javax.management.loading;
009:
010: import java.net.URLClassLoader;
011: import java.net.URL;
012: import java.net.URLStreamHandlerFactory;
013: import java.net.MalformedURLException;
014: import java.util.Set;
015: import java.util.Arrays;
016: import java.util.Iterator;
017: import java.util.ArrayList;
018: import java.util.List;
019: import java.util.HashSet;
020: import java.io.File;
021: import java.io.InputStream;
022: import javax.management.ServiceNotFoundException;
023: import javax.management.MBeanRegistration;
024: import javax.management.ObjectName;
025: import javax.management.MBeanServer;
026: import javax.management.MBeanServerFactory;
027: import javax.management.MBeanException;
028: import javax.management.JMRuntimeException;
029: import javax.management.ObjectInstance;
030:
031: import org.huihoo.jfox.jmx.loading.MLetObject;
032: import org.huihoo.jfox.jmx.loading.MLetTags;
033: import org.huihoo.jfox.jmx.loading.MLetArgument;
034: import org.huihoo.jfox.jmx.loading.ObjectInputStreamLoader;
035: import org.huihoo.jfox.jmx.loading.PrimitiveClassLoader;
036: import org.huihoo.jfox.jmx.loading.MLetParser;
037:
038: /**
039: * Allows you to instantiate and register one or several MBeans in the MBean server
040: * coming from a remote URL. M-let is a shortcut for management applet.
041: * The m-let service does this by loading an m-let text file,
042: * which specifies information on the MBeans to be obtained.
043: * The information on each MBean is specified in a single instance of a tag,
044: * called the MLET tag. The location of the m-let text file is specified by a URL.
045: * The MLET tag has the following syntax:
046: *
047: * <MLET
048: * CODE = class | OBJECT = serfile
049: * ARCHIVE = "archiveList"
050: * [CODEBASE = codebaseURL]
051: * [NAME = mbeanname]
052: * [VERSION = version]
053: * >
054: * [arglist]
055: * </MLET>
056: *
057: * where:
058: *
059: * CODE = class
060: * This attribute specifies the full Java class name, including package name,
061: * of the MBean to be obtained. The compiled .class file of the MBean must be
062: * contained in one of the .jar files specified by the ARCHIVE attribute.
063: * Either CODE or OBJECT must be present.
064: * OBJECT = serfile
065: * This attribute specifies the .ser file that contains a serialized representation
066: * of the MBean to be obtained. This file must be contained in one of the .jar files
067: * specified by the ARCHIVE attribute. If the .jar file contains a directory hierarchy,
068: * specify the path of the file within this hierarchy. Otherwise a match will not be
069: * found. Either CODE or OBJECT must be present.
070: * ARCHIVE = "archiveList"
071: * This mandatory attribute specifies one or more .jar files containing MBeans or
072: * other resources used by the MBean to be obtained. One of the .jar files must
073: * contain the file specified by the CODE or OBJECT attribute. If archivelist contains
074: * more than one file:
075: * Each file must be separated from the one that follows it by a comma (,).
076: * archivelist must be enclosed in double quote marks.
077: * All .jar files in archivelist must be stored in the directory specified by the code base URL.
078: * CODEBASE = codebaseURL
079: * This optional attribute specifies the code base URL of the MBean to be obtained.
080: * It identifies the directory that contains the .jar files specified by the ARCHIVE
081: * attribute. Specify this attribute only if the .jar files are not in the same directory
082: * as the m-let text file. If this attribute is not specified, the base URL of the m-let text file is used.
083: * NAME = mbeanname
084: * This optional attribute specifies the object name to be assigned to the MBean instance
085: * when the m-let service registers it. If mbeanname starts with the colon character (:),
086: * the domain part of the object name is the domain of the agent. The m-let service
087: * invokes the getDomain() method of the Framework class to obtain this information.
088: * VERSION = version
089: * This optional attribute specifies the version number of the MBean and associated .jar
090: * files to be obtained. This version number can be used to specify that the .jar files
091: * are loaded from the server to update those stored locally in the cache the next time
092: * the m-let text file is loaded. version must be a series of non-negative decimal
093: * integers each separated by a period from the one that precedes it.
094: * arglist
095: * This optional attribute specifies a list of one or more parameters for the MBean to
096: * be instantiated. This list describes the parameters to be passed the MBean's
097: * constructor. Use the following syntax to specify each item in arglist:
098: *
099: * <ARG TYPE=argumentType VALUE=value>
100: *
101: * where:
102: * argumentType is the type of the argument that will be passed as parameter to the
103: * MBean's constructor.
104: * The arguments' type in the argument list should be a Java primitive type or a Java
105: * basic type (java.lang.Boolean, java.lang.Byte, java.lang.Short, java.lang.Long,
106: * java.lang.Integer, java.lang.Float, java.lang.Double, java.lang.String).
107: *
108: * When an m-let text file is loaded, an instance of each MBean specified in the file is
109: * created and registered.
110: * The m-let Service extends the java.net.URLClassLoader and can be used to load remote
111: * classes and jar files in the VM of the agent.
112: *
113: * Note - The MLet class loader uses the
114: * MBeanServerFactory.getClassLoaderRepository(javax.management.MBeanServer) to load
115: * classes that could not be found in the loaded jar files.
116: *
117: * @author <a href="mailto:young_yy@hotmail.org">Young Yang</a>
118: */
119:
120: public class MLet extends URLClassLoader implements MLetMBean,
121: MBeanRegistration {
122: private String liberaryDirectory = System.getProperty("user.dir");
123:
124: protected MBeanServer server = null;
125: private ObjectName objectName = null;
126: private ClassLoaderRepository clrepo = null;
127:
128: private MLetParser parser = MLetParser.getInstance();
129: protected PrimitiveClassLoader paramLoader = PrimitiveClassLoader
130: .getInstance();
131:
132: public MLet() {
133: this (new URL[0]);
134: }
135:
136: public MLet(URL[] urls) {
137: super (urls);
138: }
139:
140: public MLet(URL[] urls, ClassLoader parent) {
141: super (urls, parent);
142: }
143:
144: public MLet(URL[] urls, ClassLoader parent,
145: URLStreamHandlerFactory factory) {
146: super (urls, parent, factory);
147: }
148:
149: public Set getMBeansFromURL(URL url)
150: throws ServiceNotFoundException {
151: if (server == null)
152: throw new IllegalStateException(
153: "This MLet MBean is not registered with an MBeanServer.");
154: if (url == null)
155: throw new ServiceNotFoundException(
156: "Can not load MBeans from null URL");
157: Set mbeanInstances = new HashSet();
158: String urlString = url.toString();
159: String defaultCodeBase = urlString.substring(0, urlString
160: .lastIndexOf('/') + 1);
161: try {
162: Iterator mlets = parser.parse(url);
163: while (mlets.hasNext()) {
164: MLetObject mlet = (MLetObject) mlets.next();
165: URL[] urls = this .getMLetURLs(mlet, defaultCodeBase);
166: for (int i = 0; i < urls.length; i++) {
167: this .addURL(urls[i]);
168: }
169: mbeanInstances.add(createMBean(mlet));
170: }
171: } catch (MBeanException e) {
172:
173: throw new JMRuntimeException(e.toString());
174: }
175: return mbeanInstances;
176: }
177:
178: public Set getMBeansFromURL(String url)
179: throws ServiceNotFoundException {
180: try {
181: URL urlObj = new URL(url);
182: return this .getMBeansFromURL(urlObj);
183: } catch (MalformedURLException e) {
184: throw new ServiceNotFoundException(
185: "The specified URL is malformed");
186: }
187: }
188:
189: public void addURL(URL url) {
190: if (!Arrays.asList(getURLs()).contains(url))
191: super .addURL(url);
192: }
193:
194: public void addURL(String url) throws ServiceNotFoundException {
195: try {
196: URL urlObj = new URL(url);
197: this .addURL(urlObj);
198: } catch (MalformedURLException e) {
199: throw new ServiceNotFoundException(
200: "The specified URL is malformed");
201: }
202: }
203:
204: public String getLibraryDirectory() {
205: return liberaryDirectory;
206: }
207:
208: public void setLibraryDirectory(String ld) {
209: liberaryDirectory = ld;
210: }
211:
212: /**
213: * get server & objectName & classLoaderRepository
214: * @param server
215: * @param objName
216: * @return
217: * @throws Exception
218: */
219: public ObjectName preRegister(MBeanServer server, ObjectName objName)
220: throws Exception {
221: this .server = server;
222: if (objName == null)
223: objName = new ObjectName(server.getDefaultDomain() + ":"
224: + "service=" + this .toString());
225: this .objectName = objName;
226: clrepo = MBeanServerFactory.getClassLoaderRepository(server);
227: return objectName;
228: }
229:
230: public void postRegister(Boolean success) {
231: if (!success.booleanValue())
232: server = null;
233: }
234:
235: public void preDeregister() throws Exception {
236:
237: }
238:
239: public void postDeregister() {
240:
241: }
242:
243: /**
244: * doCreate a MBean by the offered mlet
245: *
246: * @param mlet
247: * @return ObjectInstance of the created MBean
248: */
249: protected ObjectInstance createMBean(MLetObject mlet) {
250: ObjectInstance instance = null;
251: Object mbean = null;
252: String className = mlet.getAttribute(MLetTags.CODE_ATTR);
253: try {
254: if (className != null) { // specified classname
255: MLetArgument[] args = mlet.getArguments();
256: if (args == null || args.length == 0) {
257: mbean = server.instantiate(className, objectName);
258: } else {
259: Object[] params = new Object[args.length];
260: String[] signatures = new String[args.length];
261: for (int i = 0; i < args.length; i++) {
262: // doCreate param object
263: params[i] = paramLoader.createObject(args[i]
264: .getValue(), args[i].getType());
265: signatures[i] = args[i].getType();
266: }
267:
268: mbean = server.instantiate(className, objectName,
269: params, signatures);
270: }
271: } else { // must specified the serialized object
272: String serFile = mlet
273: .getAttribute(MLetTags.OBJECT_ATTR);
274: InputStream is = getResourceAsStream(serFile);
275: if (is == null)
276: throw new ServiceNotFoundException(
277: "Cannot find MBean " + serFile
278: + " in this ClassLoader classpath");
279: ObjectInputStreamLoader loader = new ObjectInputStreamLoader(
280: is, this );
281: mbean = loader.readObject();
282:
283: }
284: ObjectName objName = null;
285: // if objName is null,the mbean must implements MBeanRegistry,provide the DEFAULT_OBJECTNAME bye preRegister
286: String oname = mlet.getAttribute(MLetTags.NAME_ATTR);
287: if (oname != null && oname.trim().length() != 0)
288: objName = new ObjectName(oname);
289: instance = server.registerMBean(mbean, objName);
290: } catch (Exception e) {
291: e.printStackTrace();
292: }
293:
294: return instance;
295: }
296:
297: /**
298: * get the jar urls, so can add it the the classloader path
299: */
300: protected URL[] getMLetURLs(MLetObject mlet, String defaultCodeBase) {
301: List urls = new ArrayList();
302: String codeBase = mlet.getAttribute(MLetTags.CODEBASE_ATTR);
303: codeBase = codeBase == null ? defaultCodeBase : defaultCodeBase
304: + codeBase;
305: if (!codeBase.endsWith("/"))
306: codeBase += "/";
307: String archive = mlet.getAttribute(MLetTags.ARCHIVE_ATTR); // maybe "a.jar,b.jar"
308: String[] archives = decompoundArchive(archive);
309:
310: for (int i = 0; i < archives.length; i++) {
311: String arch = archives[i];
312: if (!arch.startsWith("/"))
313: arch = codeBase + arch;
314: URL url = null;
315: try {
316: url = new URL(arch);
317: } catch (MalformedURLException e) {
318: // maybe cause by not specified the file:/ protocal
319: File file = new File(arch);
320: try {
321: if (file.exists())
322: url = file.toURL();
323: } catch (Exception ignore) {
324: ignore.printStackTrace();
325: }
326: }
327: if (url != null)
328: urls.add(url);
329: }
330: return (URL[]) urls.toArray(new URL[0]);
331: }
332:
333: public Class loadClass(String name) throws ClassNotFoundException {
334: Class cla = null;
335: try {
336: cla = super .loadClass(name);
337: } catch (ClassNotFoundException e) {
338: try {
339: clrepo.loadClassWithout(this , name);
340: } catch (ClassNotFoundException ex) {
341: throw ex;
342: }
343: }
344: return cla;
345: }
346:
347: // decompound multi archives "a.jar,b.jar" to array
348: private String[] decompoundArchive(String archive) {
349: List archives = new ArrayList();
350: int start = 0;
351: int commaIndex = -1;
352: int pointer = -1;
353:
354: while ((pointer = archive.indexOf(",", start)) > 0) { // find comma
355: archives.add(archive.substring(start, pointer).trim());
356: commaIndex = pointer;
357: start = pointer + 1;
358: }
359: if (commaIndex == -1) { // not found comma
360: archives.add(archive);
361: } else {
362: archives.add(archive.substring(commaIndex + 1).trim());
363: }
364: return (String[]) archives.toArray(new String[0]);
365: }
366:
367: public static void main(String[] args) throws Exception {
368: new MLet()
369: .getMBeansFromURL("file:/D:/JBoss/conf/default/jboss.conf");
370: // System.out.println(System.mapLibraryName("test"));
371: }
372: }
|