001: /* Copyright 2002 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal.car;
007:
008: import java.io.IOException;
009: import java.io.InputStream;
010: import java.net.URL;
011: import java.security.AccessController;
012: import java.security.PrivilegedActionException;
013: import java.security.PrivilegedExceptionAction;
014: import java.security.SecureClassLoader;
015: import java.util.StringTokenizer;
016:
017: import org.apache.commons.logging.Log;
018: import org.apache.commons.logging.LogFactory;
019:
020: /**
021: * Loads classes and resources from installed CARs via the CarResources class.
022: * If classes are visible via the parent class loader then they will be used
023: * in place of those in the CARs. This is a singleton so that we have a single
024: * unified class namespace for all car resources preventing linkage errors and
025: * class cast exceptions.
026: * @author Mark Boyd {@link <a href="mailto:mark.boyd@engineer.com">mark.boyd@engineer.com</a>}
027: * @version $Revision: 36690 $
028: */
029: public class CarClassLoader extends SecureClassLoader {
030: public final static String RCS_ID = "@(#) $Header$";
031:
032: private static final Log log = LogFactory
033: .getLog(CarClassLoader.class);
034:
035: /**
036: Create a CarClassLoader. This method has package scope so that
037: CarResources can instantiate it and hold the single instance to be
038: aquired via its getClassLoader() method.
039: */
040: CarClassLoader() {
041: super ();
042: }
043:
044: /**
045: Create a CarClassloader with the indicated parent class loader. See
046: comment for zero parameter constructor for description of package scoping.
047: */
048: CarClassLoader(ClassLoader cl) {
049: super (cl);
050: }
051:
052: /**
053: Implement the overloading of findClass to return classes that are
054: available from installed CAR files. Class loading precedes with the
055: parent classloader first which delegates to this class loader if the
056: classes aren't found.
057: */
058: public Class findClass(final String name)
059: throws ClassNotFoundException {
060: PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
061: public Object run() throws ClassNotFoundException {
062: byte[] buf = null;
063: String pkgName = getPackageName(name);
064: InputStream in = null;
065: try {
066: String file = name.replace('.', '/') + ".class";
067: CarResources crs = CarResources.getInstance();
068: int size = (int) crs.getResourceSize(file);
069: in = crs.getResourceAsStream(file);
070:
071: if (in == null || size == -1)
072: throw new Exception("Car resource " + file
073: + " not found.");
074:
075: buf = new byte[size];
076: int offSet = 0;
077: int totalRead = 0;
078: int bytesRead = 0;
079: int remaining = size;
080:
081: while (totalRead < size) {
082: bytesRead = in.read(buf, offSet, remaining);
083: remaining -= bytesRead;
084: offSet += bytesRead;
085: totalRead += bytesRead;
086: }
087: } catch (Exception e) {
088: throw new ClassNotFoundException(name, e);
089: } finally {
090: try {
091: if (in != null) {
092: in.close();
093: }
094: } catch (IOException ioe) {
095: log
096: .error("CarClassLoader::findClass() Could not close inputStream "
097: + ioe);
098: }
099: }
100:
101: // package must be defined prior to defined
102: // the class.
103: createPackage(pkgName);
104:
105: return defineTheClass(name, buf, 0, buf.length);
106: }
107: };
108: try {
109: return (Class) AccessController.doPrivileged(action);
110: } catch (PrivilegedActionException pae) {
111: throw (ClassNotFoundException) pae.getException();
112: }
113: }
114:
115: /**
116: Create and return the Class object from the passed in class bytes. This
117: code enables the inner class used in findClass() to call into the
118: superclass's defineClass method. It has protected scope in the
119: superclass and hence is not visible to an innner class but is visible
120: to this class.
121: */
122: private Class defineTheClass(String n, byte[] b, int offset, int len) {
123: return super .defineClass(n, b, offset, len);
124: }
125:
126: /**
127: * Creates the package name for the calling class, which is null
128: * by default based on the JavaDoc for ClassLoader. The package
129: * must be created prior to defining the Class.
130: *
131: * @param pkgName the package to create.
132: **/
133: private void createPackage(String pkgName) {
134: // package must be defined before the class
135: // according to the API docs.
136: try {
137: if (null != pkgName && null == getPackage(pkgName))
138: definePackage(pkgName, "", "", "", "", "", "", null);
139: } catch (IllegalArgumentException iae) {
140: // do nothing, assume a synchronization issue
141: // where one thread had set it prior to another
142: // doing so.. small window, but could happen.
143: }
144: }
145:
146: /**
147: * Returns a package name from a package/classname path. If the
148: * package is not available (default package), then null is
149: * returned.
150: *
151: * @param name the package/class name.
152: * @return the package name (dot notation) or null if not found
153: */
154: private String getPackageName(String name) {
155: if (name.indexOf(".") != -1) {
156: StringBuffer sb = new StringBuffer();
157: StringTokenizer st = new StringTokenizer(name, ".");
158: int tokens = st.countTokens();
159: int count = 1;
160: while (st.hasMoreTokens()) {
161: if (count < tokens) {
162: sb.append(st.nextToken());
163: if (count != (tokens - 1))
164: sb.append(".");
165: } else
166: break;
167: count++;
168: }
169: return sb.toString();
170: } else
171: return null;
172: }
173:
174: /**
175: Returns a URL pointing to a car resource if a suitable resource is
176: found in the loaded set of CAR files or null if one is not found.
177: */
178: public URL findResource(String res) {
179: return CarResources.getInstance().findResource(res);
180: }
181: }
|