001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.flow.javascript.fom;
018:
019: import org.apache.excalibur.source.Source;
020: import org.apache.excalibur.source.SourceResolver;
021: import org.tempuri.javac.JavaClassReader;
022: import org.tempuri.javac.JavaClassReaderFactory;
023: import org.tempuri.javac.JavaClassWriter;
024: import org.tempuri.javac.JavaClassWriterFactory;
025: import org.tempuri.javac.JavaCompiler;
026: import org.tempuri.javac.JavaCompilerErrorHandler;
027: import org.tempuri.javac.JavaSourceReader;
028: import org.tempuri.javac.JavaSourceReaderFactory;
029: import org.tempuri.javacImpl.eclipse.JavaCompilerImpl;
030:
031: import java.io.ByteArrayInputStream;
032: import java.io.ByteArrayOutputStream;
033: import java.io.IOException;
034: import java.io.InputStream;
035: import java.io.InputStreamReader;
036: import java.io.Reader;
037: import java.net.MalformedURLException;
038: import java.util.HashSet;
039: import java.util.Iterator;
040: import java.util.LinkedList;
041: import java.util.List;
042:
043: /**
044: * @version CVS $Id: CompilingClassLoader.java 433543 2006-08-22 06:22:54Z crossley $
045: */
046: public class CompilingClassLoader extends ClassLoader {
047:
048: SourceResolver sourceResolver;
049: JavaCompiler compiler;
050: List sourcePath = new LinkedList();
051: HashSet sourceListeners = new HashSet();
052: ClassRepository classRepository;
053:
054: public interface SourceListener {
055: public void sourceCompiled(Source src);
056:
057: public void sourceCompilationError(Source src, String error);
058: }
059:
060: public interface ClassRepository {
061: public byte[] getCompiledClass(String className);
062:
063: public void addCompiledClass(String className, Source source,
064: byte[] contents);
065: }
066:
067: protected Class findClass(String className)
068: throws ClassNotFoundException {
069: final byte[] bytes = compile(className);
070: return defineClass(className, bytes, 0, bytes.length);
071: }
072:
073: public CompilingClassLoader(ClassLoader parent,
074: SourceResolver sourceResolver,
075: ClassRepository classRepository) {
076: super (parent);
077: this .sourceResolver = sourceResolver;
078: this .classRepository = classRepository;
079: this .compiler = new JavaCompilerImpl();
080: this .sourcePath.add("");
081: }
082:
083: static class ClassCompilationException extends
084: ClassNotFoundException {
085: public ClassCompilationException(String msg) {
086: super (msg);
087: }
088: }
089:
090: public void addSourceListener(SourceListener listener) {
091: synchronized (sourceListeners) {
092: sourceListeners.add(listener);
093: }
094: }
095:
096: public void removeSourceListener(SourceListener listener) {
097: synchronized (sourceListeners) {
098: sourceListeners.remove(listener);
099: }
100: }
101:
102: private void notifyListeners(Source src, String err) {
103: SourceListener arr[];
104: synchronized (sourceListeners) {
105: arr = new SourceListener[sourceListeners.size()];
106: sourceListeners.toArray(arr);
107: }
108: if (err != null) {
109: for (int i = 0; i < arr.length; i++) {
110: arr[i].sourceCompilationError(src, err);
111: }
112: } else {
113: for (int i = 0; i < arr.length; i++) {
114: arr[i].sourceCompiled(src);
115: }
116: }
117: }
118:
119: public void setClassRepository(ClassRepository rep) {
120: classRepository = rep;
121: }
122:
123: public ClassRepository getClassRepository() {
124: return classRepository;
125: }
126:
127: public void setSourcePath(String[] path) {
128: synchronized (sourcePath) {
129: sourcePath.clear();
130: for (int i = 0; i < path.length; i++) {
131: sourcePath.add(path[i]);
132: }
133: sourcePath.add("");
134: }
135: }
136:
137: private Source getSource(String className) {
138: int dollar = className.indexOf('$');
139: if (dollar > 0) {
140: // inner class: use the parent
141: className = className.substring(0, dollar);
142: }
143:
144: synchronized (sourcePath) {
145: Iterator i = sourcePath.iterator();
146: while (i.hasNext()) {
147: String prefix = (String) i.next();
148: if (prefix.length() > 0) {
149: if (!prefix.endsWith("/")) {
150: prefix += "/";
151: }
152: }
153: String uri = prefix + className.replace('.', '/')
154: + ".java";
155: Source src;
156: try {
157: src = sourceResolver.resolveURI(uri);
158: } catch (MalformedURLException ignored) {
159: continue;
160: } catch (IOException ignored) {
161: continue;
162: }
163:
164: if (src.exists()) {
165: return src;
166: }
167: releaseSource(src);
168: }
169: }
170:
171: return null;
172: }
173:
174: private void releaseSource(Source src) {
175: sourceResolver.release(src);
176: }
177:
178: class SourceReaderFactory implements JavaSourceReaderFactory {
179: public JavaSourceReader getSourceReader(final String className)
180: throws IOException {
181: Source src = getSource(className);
182: if (src == null)
183: return null;
184: try {
185: InputStream is = src.getInputStream();
186: if (is == null) {
187: return null;
188: }
189:
190: byte[] buf = new byte[8192];
191: ByteArrayOutputStream baos = new ByteArrayOutputStream();
192: int count;
193: while ((count = is.read(buf, 0, buf.length)) > 0) {
194: baos.write(buf, 0, count);
195: }
196: baos.flush();
197:
198: final Reader reader = new InputStreamReader(
199: new ByteArrayInputStream(baos.toByteArray()));
200: return new JavaSourceReader() {
201: public Reader getReader() {
202: return reader;
203: }
204:
205: public String getClassName() {
206: return className;
207: }
208: };
209: } finally {
210: releaseSource(src);
211: }
212: }
213: }
214:
215: protected String makeFileName(String className) {
216: Source src = getSource(className);
217: if (src != null) {
218: String result = src.getURI();
219: releaseSource(src);
220: return result;
221: }
222: return className;
223: }
224:
225: class ClassReaderFactory implements JavaClassReaderFactory {
226: public JavaClassReader getClassReader(final String className)
227: throws IOException {
228: final byte[] bytes = classRepository
229: .getCompiledClass(className);
230: if (bytes != null) {
231: return new JavaClassReader() {
232: public String getClassName() {
233: return className;
234: }
235:
236: public InputStream getInputStream() {
237: return new ByteArrayInputStream(bytes);
238: }
239: };
240: }
241:
242: String classFile = className.replace('.', '/') + ".class";
243: final InputStream is = getResourceAsStream(classFile);
244: if (is != null) {
245: return new JavaClassReader() {
246: public String getClassName() {
247: return className;
248: }
249:
250: public InputStream getInputStream() {
251: return is;
252: }
253: };
254: }
255:
256: return null;
257: }
258: }
259:
260: class ClassWriterFactory implements JavaClassWriterFactory {
261: public JavaClassWriter getClassWriter(final String className) {
262: return new JavaClassWriter() {
263: public String getClassName() {
264: return className;
265: }
266:
267: public void writeClass(InputStream contents)
268: throws IOException {
269: byte[] buf = new byte[2048];
270: ByteArrayOutputStream s = new ByteArrayOutputStream();
271:
272: int count;
273: while ((count = contents.read(buf, 0, buf.length)) > 0) {
274: s.write(buf, 0, count);
275: }
276: s.flush();
277:
278: System.out.println("Compiled: " + className);
279: Source src = getSource(className);
280: classRepository.addCompiledClass(className, src, s
281: .toByteArray());
282: notifyListeners(src, null);
283: releaseSource(src);
284: }
285: };
286: }
287: }
288:
289: class ErrorHandler implements JavaCompilerErrorHandler {
290: List errList = new LinkedList();
291:
292: public void handleError(String className, int line, int column,
293: Object errorMessage) {
294: String msg = className;
295: try {
296: // try to it convert to a file name
297: msg = makeFileName(className);
298: } catch (Exception ignored) {
299: // oh well, I tried
300: }
301:
302: if (line > 0) {
303: msg += ": Line " + line;
304: }
305: if (column >= 0) {
306: msg += "." + column;
307: }
308: msg += ": ";
309: msg += errorMessage;
310: errList.add(msg);
311: }
312:
313: public List getErrorList() {
314: return errList;
315: }
316: }
317:
318: private byte[] compile(String className)
319: throws ClassNotFoundException {
320: byte[] result = classRepository.getCompiledClass(className);
321: if (result != null) {
322: return result;
323: }
324:
325: Source src = getSource(className);
326: if (src == null) {
327: throw new ClassNotFoundException(className);
328: }
329:
330: try {
331: // try to compile it
332: ErrorHandler errorHandler = new ErrorHandler();
333: compiler.compile(new String[] { className },
334: new SourceReaderFactory(),
335: new ClassReaderFactory(), new ClassWriterFactory(),
336: errorHandler);
337: List errorList = errorHandler.getErrorList();
338: if (errorList.size() > 0) {
339: StringBuffer msg = new StringBuffer(
340: "Failed to compile Java class ");
341: msg.append(className);
342: msg.append(": ");
343: Iterator iter = errorList.iterator();
344: while (iter.hasNext()) {
345: msg.append("\n");
346: msg.append((String) iter.next());
347: }
348: notifyListeners(src, msg.toString());
349: throw new ClassCompilationException(msg.toString());
350: }
351:
352: return classRepository.getCompiledClass(className);
353: } finally {
354: releaseSource(src);
355: }
356: }
357: }
|