blob: 3d65a033198e582e92c4822602b41ead59535289 [file] [log] [blame]
/*
* 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);
}
}
}