001: /*=============================================================================
002: * Copyright Texas Instruments 2000. All Rights Reserved.
003: *
004: * This program is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2 of the License, or (at your option) any later version.
008: *
009: * This program is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: * $ProjectHeader: OSCRIPT 0.155 Fri, 20 Dec 2002 18:34:22 -0800 rclark $
019: */
020:
021: package oscript.compiler;
022:
023: import oscript.OscriptInterpreter;
024: import oscript.fs.AbstractFile;
025: import oscript.exceptions.*;
026:
027: // The Bytecode Engineerign Library
028: import org.apache.bcel.generic.*;
029: import org.apache.bcel.Constants;
030: import org.apache.bcel.classfile.JavaClass;
031:
032: import java.security.*;
033: import java.util.Iterator;
034: import java.util.LinkedList;
035:
036: import java.net.URL;
037:
038: /**
039: * A helper to create loaded java classes from a <code>JavaClass</code>
040: * instance.
041: * <p>
042: * Classes created with this class-loader are also stored to
043: * <code>.cache</code> so they will be available to future instances
044: * of the interpreter... note: I may change this in the future, I would
045: * like to make sure that the classes are stored in the same file as
046: * <code>cache.db</code>...
047: *
048: * @author Rob Clark (rob@ti.com)
049: * <!--$Format: " * @version $Revision$"$-->
050: * @version 1.10
051: */
052: public class CompilerClassLoader extends ClassLoader {
053: private static CompilerClassLoader loader;
054:
055: private static LinkedList loaderList = new LinkedList();
056: private static ClassLoader[] loaders;
057:
058: //////////////////////////////////////////////////////////////////////////////
059: // attempt to see if avoiding Class.forName helps:
060: private static java.util.Map classCache = new java.util.WeakHashMap();
061: private static ClassNotFoundException CLASS_NOT_FOUND_EXCEPTION = new ClassNotFoundException();
062:
063: public synchronized static Class forName(String className,
064: boolean initialize, ClassLoader loader)
065: throws ClassNotFoundException {
066: if (loader == null)
067: loader = getCompilerClassLoader();
068:
069: className = className.intern();
070: Object obj = classCache.get(className);
071:
072: if (obj == Boolean.FALSE)
073: throw CLASS_NOT_FOUND_EXCEPTION;
074:
075: Class javaClass = (Class) obj;
076:
077: if (javaClass == null) {
078: try {
079: javaClass = Class
080: .forName(className, initialize, loader);
081: classCache.put(className, javaClass);
082: } catch (ClassNotFoundException e) {
083: classCache.put(className, Boolean.FALSE);
084: throw e;
085: }
086: }
087:
088: return javaClass;
089: }
090:
091: //////////////////////////////////////////////////////////////////////////////
092:
093: public synchronized static CompilerClassLoader getCompilerClassLoader() {
094: if (loader == null)
095: loader = new CompilerClassLoader();
096: return loader;
097: }
098:
099: /**
100: * Class Constructor, private to enforce singleton pattern
101: */
102: private CompilerClassLoader() {
103: super (CompilerClassLoader.class.getClassLoader());
104:
105: Policy.setPolicy(new Policy() {
106:
107: public PermissionCollection getPermissions(CodeSource cs) {
108: Permissions p = new Permissions();
109: p.add(new AllPermission());
110: return p;
111: }
112:
113: public void refresh() {
114: }
115:
116: });
117: }
118:
119: /**
120: * Register a class loader that we can delegate the act of resolving
121: * classes. This allows the user of ObjectScript to give us the
122: * ability to load classes that we might not otherwise have access
123: * to.
124: * <p>
125: * Don't call this directly.. it might move. Use
126: * {@link OscriptInterpreter#registerClassLoader(ClassLoader)}
127: */
128: public static void registerClassLoader(ClassLoader loader) {
129: synchronized (loaderList) {
130: loaderList.add(loader);
131: loaders = null;
132: }
133: }
134:
135: private static ClassLoader[] getLoaders() {
136: synchronized (loaderList) {
137: if (loaders == null)
138: loaders = (ClassLoader[]) loaderList
139: .toArray(new ClassLoader[loaderList.size()]);
140: return loaders;
141: }
142: }
143:
144: /**
145: * Override <code>findClass</code> to search all registered class loader
146: * delegates.
147: */
148: protected Class findClass(String name)
149: throws ClassNotFoundException {
150: ClassNotFoundException err = CLASS_NOT_FOUND_EXCEPTION;
151: ClassLoader[] loaders = getLoaders();
152: for (int i = 0; i < loaders.length; i++) {
153: ClassLoader loader = loaders[i];
154: try {
155: return loader.loadClass(name);
156: } catch (ClassNotFoundException e) {
157: err = e;
158: }
159: }
160: throw err;
161: }
162:
163: /**
164: * Override <code>findResource</code> to search all registered
165: * class loader delegates.
166: */
167: public URL findResource(String name) {
168: URL url = super .findResource(name);
169: ClassLoader[] loaders = getLoaders();
170: for (int i = 0; i < loaders.length && (url == null); i++)
171: url = loaders[i].getResource(name);
172: return url;
173: }
174:
175: /**
176: */
177: private static String toFileName(String className) {
178: return className.replace('.', '/') + ".class";
179: }
180:
181: /**
182: * In order to ensure that new classes are not generated with the same
183: * name as a previously generated class, the process of selecting the
184: * the name for the class to generate is delegated to this class-loader.
185: * This is a wierd thing for a class loader to do, but maybe I can think
186: * of a cleaner way to do this...
187: *
188: * @param suggestedClassName is used to seed the class name generation, so
189: * that the name that is generated sort of "makes sense"
190: */
191: public String makeClassName(String suggestedClassName) {
192: return makeClassName(suggestedClassName, false);
193: }
194:
195: public synchronized String makeClassName(String suggestedClassName,
196: boolean overwrite) {
197: // any other "gotcha" characters to watch for?
198: suggestedClassName = suggestedClassName.replace('-', '_')
199: .replace(':', '_');
200:
201: // this is where we create the jar file entry... if the entry does
202: // not exist later when <code>makeClass</code> is called, that means
203: // that the name was not generated with this method, which is an
204: // error
205:
206: // find a name based on suggested-name that does not already exist:
207: while (true) {
208: try {
209: AbstractFile file = OscriptInterpreter.resolve(
210: "/cache/" + toFileName(suggestedClassName),
211: false);
212:
213: if (!file.exists() || overwrite) {
214: file.createNewFile();
215: break;
216: }
217: } catch (java.io.IOException e) {
218: throw new ProgrammingErrorException(e);
219: }
220:
221: suggestedClassName += Math
222: .abs((int) (100.0 * Math.random()));
223: }
224:
225: return suggestedClassName;
226: }
227:
228: /**
229: * Make a class... perhaps this method should take a ClassGen???
230: */
231: public synchronized Class makeClass(String className,
232: JavaClass javaClass) {
233: Class c = null;
234:
235: try {
236: AbstractFile file = OscriptInterpreter.resolve("/cache/"
237: + toFileName(className), false);
238:
239: if (!file.exists())
240: throw new ProgrammingErrorException(
241: "file does not exist! Did you forget to call makeClassName?");
242:
243: // dump the .class to a byte array, so we can immediately instantiate:
244: java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
245: javaClass.dump(bos);
246: byte[] bytes = bos.toByteArray();
247:
248: // save to file:
249: java.io.OutputStream os = file.getOutputStream(false);
250: os.write(bytes);
251: os.flush();
252: os.close();
253:
254: // load the class from the byte array:
255: c = defineClass(className, bytes, 0, bytes.length);
256: } catch (Exception e) {
257: // this shouldn't actually happen:
258: e.printStackTrace();
259: }
260:
261: return c;
262: }
263: }
264:
265: /*
266: * Local Variables:
267: * tab-width: 2
268: * indent-tabs-mode: nil
269: * mode: java
270: * c-indentation-style: java
271: * c-basic-offset: 2
272: * eval: (c-set-offset 'substatement-open '0)
273: * End:
274: */
|