/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.cocoon.components.flow.javascript.fom; import org.apache.excalibur.source.Source; import org.apache.excalibur.source.SourceResolver; import org.tempuri.javac.JavaClassReader; import org.tempuri.javac.JavaClassReaderFactory; import org.tempuri.javac.JavaClassWriter; import org.tempuri.javac.JavaClassWriterFactory; import org.tempuri.javac.JavaCompiler; import org.tempuri.javac.JavaCompilerErrorHandler; import org.tempuri.javac.JavaSourceReader; import org.tempuri.javac.JavaSourceReaderFactory; import org.tempuri.javacImpl.eclipse.JavaCompilerImpl; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.net.MalformedURLException; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; /** * @version CVS $Id$ */ public class CompilingClassLoader extends ClassLoader { SourceResolver sourceResolver; JavaCompiler compiler; List sourcePath = new LinkedList(); HashSet sourceListeners = new HashSet(); ClassRepository classRepository; public interface SourceListener { public void sourceCompiled(Source src); public void sourceCompilationError(Source src, String error); } public interface ClassRepository { public byte[] getCompiledClass(String className); public void addCompiledClass(String className, Source source, byte[] contents); } protected Class findClass(String className) throws ClassNotFoundException { final byte[] bytes = compile(className); return defineClass(className, bytes, 0, bytes.length); } public CompilingClassLoader(ClassLoader parent, SourceResolver sourceResolver, ClassRepository classRepository) { super(parent); this.sourceResolver = sourceResolver; this.classRepository = classRepository; this.compiler = new JavaCompilerImpl(); this.sourcePath.add(""); } static class ClassCompilationException extends ClassNotFoundException { public ClassCompilationException(String msg) { super(msg); } } public void addSourceListener(SourceListener listener) { synchronized (sourceListeners) { sourceListeners.add(listener); } } public void removeSourceListener(SourceListener listener) { synchronized (sourceListeners) { sourceListeners.remove(listener); } } private void notifyListeners(Source src, String err) { SourceListener arr[]; synchronized (sourceListeners) { arr = new SourceListener[sourceListeners.size()]; sourceListeners.toArray(arr); } if (err != null) { for (int i = 0; i < arr.length; i++) { arr[i].sourceCompilationError(src, err); } } else { for (int i = 0; i < arr.length; i++) { arr[i].sourceCompiled(src); } } } public void setClassRepository(ClassRepository rep) { classRepository = rep; } public ClassRepository getClassRepository() { return classRepository; } public void setSourcePath(String[] path) { synchronized (sourcePath) { sourcePath.clear(); for (int i = 0; i < path.length; i++) { sourcePath.add(path[i]); } sourcePath.add(""); } } private Source getSource(String className) { int dollar = className.indexOf('$'); if (dollar > 0) { // inner class: use the parent className = className.substring(0, dollar); } synchronized (sourcePath) { Iterator i = sourcePath.iterator(); while (i.hasNext()) { String prefix = (String) i.next(); if (prefix.length() > 0) { if (!prefix.endsWith("/")) { prefix += "/"; } } String uri = prefix + className.replace('.', '/') + ".java"; Source src; try { src = sourceResolver.resolveURI(uri); } catch (MalformedURLException ignored) { continue; } catch (IOException ignored) { continue; } if (src.exists()) { return src; } releaseSource(src); } } return null; } private void releaseSource(Source src) { sourceResolver.release(src); } class SourceReaderFactory implements JavaSourceReaderFactory { public JavaSourceReader getSourceReader(final String className) throws IOException { Source src = getSource(className); if (src == null) return null; try { InputStream is = src.getInputStream(); if (is == null) { return null; } byte[] buf = new byte[8192]; ByteArrayOutputStream baos = new ByteArrayOutputStream(); int count; while ((count = is.read(buf, 0, buf.length)) > 0) { baos.write(buf, 0, count); } baos.flush(); final Reader reader = new InputStreamReader(new ByteArrayInputStream(baos.toByteArray())); return new JavaSourceReader() { public Reader getReader() { return reader; } public String getClassName() { return className; } }; } finally { releaseSource(src); } } } protected String makeFileName(String className) { Source src = getSource(className); if (src != null) { String result = src.getURI(); releaseSource(src); return result; } return className; } class ClassReaderFactory implements JavaClassReaderFactory { public JavaClassReader getClassReader(final String className) throws IOException { final byte[] bytes = classRepository.getCompiledClass(className); if (bytes != null) { return new JavaClassReader() { public String getClassName() { return className; } public InputStream getInputStream() { return new ByteArrayInputStream(bytes); } }; } String classFile = className.replace('.', '/') + ".class"; final InputStream is = getResourceAsStream(classFile); if (is != null) { return new JavaClassReader() { public String getClassName() { return className; } public InputStream getInputStream() { return is; } }; } return null; } } class ClassWriterFactory implements JavaClassWriterFactory { public JavaClassWriter getClassWriter(final String className) { return new JavaClassWriter() { public String getClassName() { return className; } public void writeClass(InputStream contents) throws IOException { byte[] buf = new byte[2048]; ByteArrayOutputStream s = new ByteArrayOutputStream(); int count; while ((count = contents.read(buf, 0, buf.length)) > 0) { s.write(buf, 0, count); } s.flush(); System.out.println("Compiled: " + className); Source src = getSource(className); classRepository.addCompiledClass(className, src, s.toByteArray()); notifyListeners(src, null); releaseSource(src); } }; } } class ErrorHandler implements JavaCompilerErrorHandler { List errList = new LinkedList(); public void handleError(String className, int line, int column, Object errorMessage) { String msg = className; try { // try to it convert to a file name msg = makeFileName(className); } catch (Exception ignored) { // oh well, I tried } if (line > 0) { msg += ": Line " + line; } if (column >= 0) { msg += "." + column; } msg += ": "; msg += errorMessage; errList.add(msg); } public List getErrorList() { return errList; } } private byte[] compile(String className) throws ClassNotFoundException { byte[] result = classRepository.getCompiledClass(className); if (result != null) { return result; } Source src = getSource(className); if (src == null) { throw new ClassNotFoundException(className); } try { // try to compile it ErrorHandler errorHandler = new ErrorHandler(); compiler.compile(new String[] {className}, new SourceReaderFactory(), new ClassReaderFactory(), new ClassWriterFactory(), errorHandler); List errorList = errorHandler.getErrorList(); if (errorList.size() > 0) { StringBuffer msg = new StringBuffer("Failed to compile Java class "); msg.append(className); msg.append(": "); Iterator iter = errorList.iterator(); while (iter.hasNext()) { msg.append("\n"); msg.append((String)iter.next()); } notifyListeners(src, msg.toString()); throw new ClassCompilationException(msg.toString()); } return classRepository.getCompiledClass(className); } finally { releaseSource(src); } } }