001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.java.gen;
031:
032: import com.caucho.java.JavaCompiler;
033: import com.caucho.java.JavaWriter;
034: import com.caucho.loader.DynamicClassLoader;
035: import com.caucho.loader.SimpleLoader;
036: import com.caucho.loader.enhancer.EnhancingClassLoader;
037: import com.caucho.server.util.CauchoSystem;
038: import com.caucho.util.CharBuffer;
039: import com.caucho.util.L10N;
040: import com.caucho.vfs.MergePath;
041: import com.caucho.vfs.Path;
042: import com.caucho.vfs.Vfs;
043: import com.caucho.vfs.WriteStream;
044:
045: import java.io.IOException;
046: import java.lang.reflect.Method;
047: import java.lang.reflect.Constructor;
048: import java.util.ArrayList;
049: import java.util.logging.Level;
050: import java.util.logging.Logger;
051:
052: /**
053: * Factory for generating Java classes.
054: */
055: public class JavaClassGenerator {
056: private static final L10N L = new L10N(JavaClassGenerator.class);
057: private static final Logger log = Logger
058: .getLogger(JavaClassGenerator.class.getName());
059:
060: // Parent class loader
061: private ClassLoader _parentLoader;
062: // class loader
063: private ClassLoader _loader;
064:
065: private String _encoding;
066:
067: // The search path
068: private Path _searchPath;
069:
070: // The work directory
071: private Path _workPath;
072:
073: private ArrayList<String> _pendingFiles = new ArrayList<String>();
074:
075: private String _initMethod = "_caucho_init";
076: private String _isModifiedMethod = "_caucho_is_modified";
077:
078: /**
079: * Sets the full generated class.
080: */
081: public static String cleanClassName(String className) {
082: StringBuilder cb = new StringBuilder();
083:
084: for (int i = 0; i < className.length(); i++) {
085: char ch = className.charAt(i);
086:
087: if (ch == '.' || ch == '/')
088: cb.append('.');
089: else if (Character.isJavaIdentifierPart(ch))
090: cb.append(ch);
091: else
092: cb.append('_');
093: }
094:
095: return cb.toString();
096: }
097:
098: /**
099: * Returns the default merge path
100: */
101: public static Path getDefaultSearchPath() {
102: MergePath mergePath = new MergePath();
103:
104: mergePath.addMergePath(Vfs.lookup());
105: mergePath.addClassPath();
106:
107: return mergePath;
108: }
109:
110: /**
111: * Sets the search path.
112: */
113: public void setSearchPath(Path path) {
114: _searchPath = path;
115: }
116:
117: /**
118: * Returns the assigned search path.
119: */
120: public Path getSearchPath() {
121: return _searchPath;
122: }
123:
124: public void setEncoding(String encoding) {
125: _encoding = encoding;
126: }
127:
128: /**
129: * Sets the parent class loader.
130: *
131: * @param loader parent class loader
132: */
133: public void setParentLoader(ClassLoader loader) {
134: if (loader instanceof EnhancingClassLoader) {
135: EnhancingClassLoader enhancingLoader = (EnhancingClassLoader) loader;
136: }
137:
138: _parentLoader = loader;
139: }
140:
141: /**
142: * Sets the class loader.
143: *
144: * @param loader parent class loader
145: */
146: public void setLoader(ClassLoader loader) {
147: _loader = loader;
148: }
149:
150: /**
151: * Gets the class loader.
152: */
153: public ClassLoader getClassLoader() {
154: return _loader;
155: /*
156: if (_loader == null) {
157: _loader = SimpleLoader.create(getParentLoader(),
158: getWorkDir());
159: }
160:
161: return _loader;
162: */
163: }
164:
165: /**
166: * Sets the parent class loader.
167: *
168: * @return the parent class loader
169: */
170: public ClassLoader getParentLoader() {
171: if (_parentLoader == null)
172: _parentLoader = Thread.currentThread()
173: .getContextClassLoader();
174:
175: return _parentLoader;
176: }
177:
178: /**
179: * Sets the parent class loader.
180: *
181: * @return the parent class loader
182: */
183: public ClassLoader getPreloadLoader() {
184: return Thread.currentThread().getContextClassLoader();
185:
186: /*
187: if (_preloadLoader instanceof EnhancingClassLoader) {
188: EnhancingClassLoader enhancingLoader;
189: enhancingLoader = (EnhancingClassLoader) _preloadLoader;
190: _preloadLoader = enhancingLoader.getRawLoader();
191: }
192: */
193: }
194:
195: /**
196: * Sets the work path for the generated class.
197: */
198: public void setWorkDir(Path workPath) {
199: _workPath = workPath;
200: }
201:
202: /**
203: * Returns the class dir for the generated class.
204: */
205: public Path getWorkDir() {
206: if (_workPath == null)
207: return CauchoSystem.getWorkPath();
208: else
209: return _workPath;
210: }
211:
212: /**
213: * Try to preload the class.
214: *
215: * @return true if the preloaded class is still valid.
216: */
217: public Class preload(String fullClassName) {
218: try {
219: Class cl = loadClass(fullClassName, true);
220:
221: if (cl != null) {
222: // force validation of the class
223: Constructor[] ctors = cl.getConstructors();
224: }
225:
226: return cl;
227: } catch (ClassNotFoundException e) {
228: log.log(Level.FINEST, e.toString(), e);
229:
230: return null;
231: } catch (ClassFormatError e) {
232: log.log(Level.FINEST, e.toString(), e);
233:
234: return null;
235: }
236: }
237:
238: /**
239: * Call to generate the java source.
240: */
241: public void generate(GenClass javaClass) throws Exception {
242: String className = javaClass.getFullClassName();
243: String javaPathName = className.replace('.', '/') + ".java";
244: String classPathName = className.replace('.', '/') + ".class";
245:
246: Path javaPath = getWorkDir().lookup(javaPathName);
247: Path classPath = getWorkDir().lookup(classPathName);
248:
249: try {
250: classPath.remove();
251: } catch (IOException e) {
252: }
253:
254: javaPath.getParent().mkdirs();
255:
256: WriteStream os = javaPath.openWrite();
257: try {
258: if (_encoding != null)
259: os.setEncoding(_encoding);
260: else
261: os.setEncoding("JAVA");
262:
263: JavaWriter out = new JavaWriter(os);
264:
265: javaClass.generate(out);
266: } finally {
267: os.close();
268: }
269:
270: _pendingFiles.add(javaPathName);
271: }
272:
273: public void addPendingFile(String javaPath) {
274: _pendingFiles.add(javaPath);
275: }
276:
277: /**
278: * Compiles the Java code
279: */
280: public Class compile(String fullClassName) throws Exception {
281: compileJava(fullClassName);
282:
283: return loadClass(fullClassName, false);
284: }
285:
286: /**
287: * Compiles the class.
288: */
289: public void compileJava(String fullClassName) throws IOException,
290: ClassNotFoundException {
291: JavaCompiler compiler = JavaCompiler.create(getPreloadLoader());
292:
293: compiler.setClassLoader(getPreloadLoader());
294: compiler.setClassDir(getWorkDir());
295:
296: if (_encoding != null)
297: compiler.setEncoding(_encoding);
298:
299: compiler.compile(fullClassName.replace('.', '/') + ".java",
300: null);
301: }
302:
303: /**
304: * Compiles the pending files
305: */
306: public void compilePendingJava() throws IOException,
307: ClassNotFoundException {
308: JavaCompiler compiler = JavaCompiler.create(getPreloadLoader());
309:
310: compiler.setClassLoader(getPreloadLoader());
311: compiler.setClassDir(getWorkDir());
312:
313: if (_encoding != null)
314: compiler.setEncoding(_encoding);
315:
316: String[] files = new String[_pendingFiles.size()];
317: _pendingFiles.toArray(files);
318: _pendingFiles.clear();
319:
320: compiler.compileBatch(files);
321: }
322:
323: /**
324: * Loads the generated class. If any class dependencies have
325: * changed, return null.
326: */
327: public Class loadClass(String fullClassName)
328: throws ClassNotFoundException {
329: return loadClass(fullClassName, false);
330: }
331:
332: /**
333: * Loads the generated class. If any class dependencies have
334: * changed, return null.
335: */
336: public Class loadClass(String fullClassName, boolean preload)
337: throws ClassNotFoundException {
338: DynamicClassLoader preloadLoader = null;
339:
340: try {
341: ClassLoader loader;
342:
343: if (preload) {
344: preloadLoader = SimpleLoader.create(getPreloadLoader(),
345: getWorkDir(), fullClassName);
346:
347: // needed for cases like Amber enhancing
348: preloadLoader.setServletHack(true);
349: loader = preloadLoader;
350: } else {
351: // XXX: because of automatic instantiation, might cause trouble
352: loader = getClassLoader();
353:
354: if (loader == null) {
355: loader = SimpleLoader.create(getParentLoader(),
356: getWorkDir(), fullClassName);
357: }
358: }
359:
360: Class cl = Class.forName(fullClassName, false, loader);
361:
362: if (cl == null)
363: return null;
364:
365: if (!preload)
366: return cl;
367:
368: if (isModified(cl))
369: return null;
370:
371: if (_loader != null)
372: loader = _loader;
373: else {
374: loader = SimpleLoader.create(getParentLoader(),
375: getWorkDir(), fullClassName);
376: }
377:
378: return Class.forName(fullClassName, false, loader);
379: } catch (RuntimeException e) {
380: if (!preload)
381: throw e;
382: else
383: return null;
384: } catch (Error e) {
385: if (!preload)
386: throw e;
387: else
388: return null;
389: } catch (ClassNotFoundException e) {
390: if (!preload)
391: throw e;
392: else
393: return null;
394: } finally {
395: if (preloadLoader != null)
396: preloadLoader.destroy();
397: }
398: }
399:
400: /**
401: * Returns true if the class is modified.
402: */
403: public boolean isModified(Class cl) {
404: Path searchPath = getSearchPath();
405:
406: if (searchPath == null)
407: searchPath = getDefaultSearchPath();
408:
409: try {
410: Method method = cl.getMethod(_initMethod,
411: new Class[] { Path.class });
412: method.invoke(null, new Object[] { searchPath });
413:
414: method = cl.getMethod(_isModifiedMethod, new Class[0]);
415: Boolean value = (Boolean) method.invoke(null,
416: new Object[] {});
417:
418: return value.booleanValue();
419: } catch (NoSuchMethodException e) {
420: return false;
421: } catch (Throwable e) {
422: log.warning(e.toString());
423:
424: return true;
425: }
426: }
427: }
|