001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package javax.management.loading;
023:
024: import java.io.Externalizable;
025: import java.io.IOException;
026: import java.io.ObjectInput;
027: import java.io.ObjectOutput;
028: import java.net.MalformedURLException;
029: import java.net.URL;
030: import java.net.URLClassLoader;
031: import java.net.URLStreamHandlerFactory;
032: import java.security.AccessController;
033: import java.security.PrivilegedActionException;
034: import java.security.PrivilegedExceptionAction;
035: import java.text.ParseException;
036: import java.util.HashSet;
037: import java.util.Iterator;
038: import java.util.Set;
039:
040: import javax.management.MBeanRegistration;
041: import javax.management.MBeanServer;
042: import javax.management.ObjectName;
043: import javax.management.ServiceNotFoundException;
044:
045: import org.jboss.logging.Logger;
046: import org.jboss.mx.loading.MBeanElement;
047: import org.jboss.mx.loading.MBeanFileParser;
048: import org.jboss.mx.loading.MLetParser;
049: import org.jboss.mx.loading.RepositoryClassLoader;
050: import org.jboss.mx.server.ServerConstants;
051: import org.jboss.mx.util.MBeanInstaller;
052: import org.jboss.mx.util.ObjectNameFactory;
053: import org.jboss.util.NestedRuntimeException;
054: import org.jboss.util.id.SerialVersion;
055:
056: /**
057: * URL classloader capable of parsing an MLet text file adhering to the file
058: * format defined in the JMX specification (v1.0).
059: *
060: * @see javax.management.loading.MLetMBean
061: *
062: * @author <a href="mailto:juha@jboss.org">Juha Lindfors</a>.
063: * @version $Revision: 57200 $
064: */
065: public class MLet extends URLClassLoader implements MLetMBean,
066: MBeanRegistration, Externalizable {
067: // Constants --------------------------------------------------------
068:
069: private static final long serialVersionUID;
070: static {
071: if (SerialVersion.version == SerialVersion.LEGACY)
072: serialVersionUID = -3671491454721196053L;
073: else
074: serialVersionUID = 3636148327800330130L;
075: }
076:
077: /**
078: * Log reference.
079: */
080: private static final Logger log = Logger.getLogger(MLet.class);
081:
082: // FIXME: (RI javadoc) Note - The MLet class loader uses the DefaultLoaderRepository
083: // to load classes that could not be found in the loaded jar files.
084: //
085: // IOW we need to override findClass for this cl...
086: // I think we can avoid the ugly dlr field hack from RI
087:
088: // Attributes ----------------------------------------------------
089:
090: /** Reference to the MBean server this loader is registered to. */
091: private MBeanServer server = null;
092:
093: /** MBean installer based on MLet version. */
094: private MBeanInstaller installer = null;
095:
096: /** Our wrapping classloader */
097: private RepositoryClassLoader rcl = null;
098:
099: /**
100: * Boolean field that indicates whether the MLet should delegate classloading
101: * to classloader repository in case the requested class is not found from
102: * its own set of loaded classes. Defaults to true.
103: */
104: private boolean delegateToCLR = true;
105:
106: /**
107: * Library directory for native libs.
108: */
109: private String libraryDir = null;
110:
111: // Static --------------------------------------------------------
112:
113: // Constructors --------------------------------------------------
114:
115: public MLet() {
116: super (new URL[0], Thread.currentThread()
117: .getContextClassLoader());
118: }
119:
120: public MLet(URL[] urls) {
121: super (urls, Thread.currentThread().getContextClassLoader());
122: }
123:
124: public MLet(URL[] urls, ClassLoader parent) {
125: super (urls, parent);
126: }
127:
128: public MLet(URL[] urls, ClassLoader parent,
129: URLStreamHandlerFactory factory) {
130: super (urls, parent, factory);
131: }
132:
133: /*
134: * JMX 1.2 spec change notes:
135: *
136: * Each constructor for the MLet class acquires an overloaded version
137: * with a booleanparameter delegateToCLR, which defaults to true. When
138: * false, the MLet does not delegate to the CLR when it fails to find
139: * classes but directly throws ClassNotFoundException.
140: */
141:
142: public MLet(URL[] urls, boolean delegateToCLR) {
143: super (urls, Thread.currentThread().getContextClassLoader());
144:
145: this .delegateToCLR = delegateToCLR;
146: }
147:
148: public MLet(URL[] urls, ClassLoader parent, boolean delegateToCLR) {
149: super (urls, parent);
150:
151: this .delegateToCLR = delegateToCLR;
152: }
153:
154: public MLet(URL[] urls, ClassLoader parent,
155: URLStreamHandlerFactory factory, boolean delegateToCLR) {
156: super (urls, parent, factory);
157:
158: this .delegateToCLR = delegateToCLR;
159: }
160:
161: // MBeanRegistration implementation ------------------------------
162:
163: public ObjectName preRegister(MBeanServer server, ObjectName name)
164: throws Exception {
165: if (name == null) {
166: String defaultDomain = server.getDefaultDomain();
167: name = new ObjectName(defaultDomain + ":type=MLet");
168: }
169:
170: this .server = server;
171: this .installer = new MBeanInstaller(server, this , name);
172:
173: return name;
174: }
175:
176: public void postRegister(Boolean registrationDone) {
177: }
178:
179: public void preDeregister() throws Exception {
180: }
181:
182: public void postDeregister() {
183: server = null;
184: rcl = null;
185: installer = null;
186: }
187:
188: // MLetMBean implementation --------------------------------------
189:
190: public Set getMBeansFromURL(String url)
191: throws ServiceNotFoundException {
192: try {
193: return getMBeansFromURL(new URL(url));
194: } catch (MalformedURLException e) {
195: throw new ServiceNotFoundException("Malformed URL:" + url);
196: }
197: }
198:
199: public Set getMBeansFromURL(URL url)
200: throws ServiceNotFoundException {
201: if (server == null)
202: throw new ServiceNotFoundException(
203: "Loader must be registered to the server before loading the MBeans.");
204:
205: HashSet mbeans = new HashSet();
206: MBeanElement element = null;
207:
208: try {
209: MBeanFileParser parser = new MLetParser();
210: Set mlets = parser.parseMBeanFile(url);
211:
212: if (mlets.size() == 0)
213: throw new ServiceNotFoundException(
214: "The specified URL '" + url
215: + "' does not contain MLET tags.");
216:
217: Iterator it = mlets.iterator();
218: while (it.hasNext()) {
219: element = (MBeanElement) it.next();
220:
221: // pass delegateToCLR property to the MBean installer
222: element.setProperty(MBeanElement.MLET_DELEGATE_TO_CLR,
223: new Boolean(delegateToCLR));
224:
225: String codebase = element.getCodebase();
226:
227: // if no codebase is specified then the url of the mlet text file is used
228: if (codebase == null)
229: codebase = url.toString().substring(0,
230: url.toString().lastIndexOf('/'));
231:
232: Iterator archives = element.getArchives().iterator();
233: String codebaseURL = null;
234:
235: while (archives.hasNext()) {
236: try {
237: codebaseURL = codebase
238: + ((codebase.endsWith("/")) ? "" : "/")
239: + archives.next();
240:
241: addURL(new URL(url, codebaseURL));
242: } catch (MalformedURLException e) {
243: log
244: .error("MLET ERROR: malformed codebase URL: '"
245: + codebaseURL + "'");
246: }
247: }
248:
249: try {
250: // FIXME: see the note at the beginning... we use an explicit loader
251: // in the createMBean() call to force this classloader to
252: // be used first to load all MLet classes. Normally this form
253: // of createMBean() call will not delegate to DLR even though
254: // the javadoc requires it. Therefore the findClass() should
255: // be overridden to delegate to the repository.
256: /*
257: mbeans.add(server.createMBean(
258: element.getCode(),
259: (element.getName() != null) ? new ObjectName(element.getName()) : null,
260: objectName,
261: element.getConstructorValues(),
262: element.getConstructorTypes())
263: );
264: */
265:
266: // installer creates or upgrades this mbean based on the MLet version
267: mbeans.add(installer.installMBean(element));
268: } catch (Throwable t) {
269: // if mbean can't be created, throwable is added to the return set
270: mbeans.add(t);
271:
272: log.error("MLET ERROR: can't create MBean: ", t);
273: }
274: }
275: } catch (ParseException e) {
276: throw new ServiceNotFoundException(e.getMessage());
277: }
278:
279: return mbeans;
280: }
281:
282: public void addURL(URL url) {
283: super .addURL(url);
284: if (rcl == null && server != null)
285: getRepositoryClassLoader();
286: rcl.addURL(url);
287: }
288:
289: public void addURL(String url) throws ServiceNotFoundException {
290: try {
291: this .addURL(new URL(url));
292: } catch (MalformedURLException e) {
293: throw new ServiceNotFoundException("Malformed URL: " + url);
294: }
295: }
296:
297: /** Returns the search path of URLs for loading classes and resources. This
298: * includes the original list of URLs specified to the constructor, along
299: * with any URLs subsequently appended by the addURL() method.
300: *
301: * @return
302: */
303: public URL[] getURLs() {
304: return super .getURLs();
305: }
306:
307: public String getLibraryDirectory() {
308: return libraryDir;
309: }
310:
311: public void setLibraryDirectory(String libdir) {
312: this .libraryDir = libdir;
313: }
314:
315: // Externalizable implementation ---------------------------------
316:
317: /* Part of JMX 1.2 specification */
318:
319: // The spec does not require implementations of the externalizable interface
320: // in which case the readExternal() and writeExternal() methods may throw
321: // an unsupported operation exception
322: /**
323: * This implementation does not support externalizing an MLet.
324: *
325: * @throws UnsupportedOperationException
326: */
327: public void readExternal(ObjectInput in) throws IOException,
328: ClassNotFoundException, UnsupportedOperationException {
329: throw new UnsupportedOperationException(
330: "MLet serialization not supported.");
331: }
332:
333: /**
334: * This implementation does not support externalizing an MLet.
335: *
336: * @throws UnsupportedOperationException
337: */
338: public void writeExternal(ObjectOutput out) throws IOException,
339: UnsupportedOperationException {
340: throw new UnsupportedOperationException(
341: "MLet serialization not supported.");
342: }
343:
344: // Classloader overrides -----------------------------------------
345:
346: /** Load a class, using the given ClassLoaderRepository if the class is not
347: * found in this MLet's URLs. The given ClassLoaderRepository can be null,
348: * in which case a ClassNotFoundException occurs immediately if the class is
349: * not found in this MLet's URLs.
350: *
351: * @param name
352: * @param clr
353: * @return
354: * @throws ClassNotFoundException
355: */
356: public Class loadClass(String name, ClassLoaderRepository clr)
357: throws ClassNotFoundException {
358: Class c = null;
359: try {
360: c = loadClass(name);
361: } catch (ClassNotFoundException e) {
362: if (clr != null)
363: c = clr.loadClass(name);
364: else
365: throw e;
366: }
367: return c;
368: }
369:
370: /** This method is to be overridden when extending this service to support
371: * caching and versioning. It is called from getMBeansFromURL when the
372: * version, codebase, and jarfile have been extracted from the MLet file,
373: * and can be used to verify that it is all right to load the given MBean,
374: * or to replace the given URL with a different one.
375: *
376: * The default implementation of this method returns codebase unchanged.
377: *
378: * @param version
379: * @param codebase
380: * @param jarfile
381: * @param mlet
382: * @return
383: * @throws Exception
384: */
385: protected URL check(String version, URL codebase, String jarfile,
386: MLetContent mlet) throws Exception {
387: return codebase;
388: }
389:
390: protected Class loadClass(String name, boolean resolve)
391: throws ClassNotFoundException {
392: boolean trace = log.isTraceEnabled();
393: if (trace)
394: log.trace("loadClass " + this + " name=" + name);
395: Class clazz = null;
396: try {
397: clazz = super .loadClass(name, resolve);
398: return clazz;
399: } finally {
400: if (trace) {
401: if (clazz != null)
402: log.trace("loadClass " + this + " name=" + name
403: + " class=" + clazz + " cl="
404: + clazz.getClassLoader());
405: else
406: log.trace("loadClass " + this + " name=" + name
407: + " not found");
408: }
409: }
410: }
411:
412: protected Class findClass(final String name)
413: throws ClassNotFoundException {
414: try {
415: return super .findClass(name);
416: } catch (ClassNotFoundException e) {
417: if (delegateToCLR) {
418: try {
419: return (Class) AccessController
420: .doPrivileged(new PrivilegedExceptionAction() {
421: public Object run()
422: throws ClassNotFoundException {
423: return server
424: .getClassLoaderRepository()
425: .loadClassBefore(MLet.this ,
426: name);
427: }
428: });
429: } catch (PrivilegedActionException pe) {
430: throw (ClassNotFoundException) pe.getException();
431: }
432: } else
433: throw e;
434: }
435: }
436:
437: protected String findLibrary(String libname) {
438: return super .findLibrary(libname);
439: }
440:
441: private void getRepositoryClassLoader() {
442: try {
443: ObjectName loader = ObjectNameFactory
444: .create(ServerConstants.DEFAULT_LOADER_NAME);
445: rcl = (RepositoryClassLoader) server.invoke(loader,
446: "getWrappingClassLoader", new Object[] { this },
447: new String[] { ClassLoader.class.getName() });
448: log.debug("MLet " + this + " using wrapper " + rcl);
449: } catch (Exception e) {
450: throw new NestedRuntimeException(e);
451: }
452: }
453: }
|