Move commons compiler to bundles

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1594135 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..b76d37e
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,27 @@
+Apache Sling Commons Java Compiler
+
+The Sling Commons Java Compiler bundle provides platform independant Java Compilation 
+support using the Eclipse Java Compiler (org.eclipse.jdt).
+
+Getting Started
+===============
+
+This component uses a Maven 2 (http://maven.apache.org/) build
+environment. It requires a Java 5 JDK (or higher) and Maven (http://maven.apache.org/)
+2.0.7 or later. We recommend to use the latest Maven version.
+
+If you have Maven 2 installed, you can compile and
+package the jar using the following command:
+
+    mvn package
+
+See the Maven 2 documentation for other build features.
+
+The latest source code for this component is available in the
+Subversion (http://subversion.tigris.org/) source repository of
+the Apache Software Foundation. If you have Subversion installed,
+you can checkout the latest source using the following command:
+
+    svn checkout http://svn.apache.org/repos/asf/sling/trunk/contrib/commons/compiler
+
+See the Subversion documentation for other source control features.
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..7638c28
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>19</version>
+        <relativePath>../../../parent/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>org.apache.sling.commons.compiler</artifactId>
+    <version>2.1.1-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+
+    <name>Apache Sling Commons Java Compiler</name>
+    <description>
+        The Sling Commons Java Compiler bundle provides platform independant Java Compilation 
+        support using the Eclipse Java Compiler (org.eclipse.jdt).
+    </description>
+
+    <scm>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/contrib/commons/compiler</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/contrib/commons/compiler</developerConnection>
+        <url>http://svn.apache.org/viewvc/sling/trunk/contrib/commons/compiler</url>
+    </scm>
+
+    <properties>
+        <sling.java.version>6</sling.java.version>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Import-Package>
+                            !org.eclipse.*,
+                            !org.apache.tools.*,*
+                        </Import-Package>
+                        <Export-Package>
+                            org.apache.sling.commons.compiler;version=2.1.0
+                        </Export-Package>
+                        <Private-Package>
+                            org.apache.sling.commons.compiler.impl
+                        </Private-Package>
+                        <Embed-Dependency>
+                            ecj
+                        </Embed-Dependency>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <reporting>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <configuration>
+                    <excludePackageNames>
+                        org.apache.sling.commons.compiler.impl
+                    </excludePackageNames>
+                </configuration>
+            </plugin>
+        </plugins>
+    </reporting>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.classloader</artifactId>
+            <version>1.3.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jdt.core.compiler</groupId>
+            <artifactId>ecj</artifactId>
+            <version>P20140317-1600</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+
+        <!-- testing -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/commons/compiler/CompilationResult.java b/src/main/java/org/apache/sling/commons/compiler/CompilationResult.java
new file mode 100644
index 0000000..4a72cbd
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/compiler/CompilationResult.java
@@ -0,0 +1,64 @@
+/*
+ * 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.sling.commons.compiler;
+
+import java.util.List;
+
+/**
+ * The compilation result allows clients of the java compiler
+ * to check for error messages, warnings (if not disabled by
+ * the options) and allows to access the compiled classes.
+ * @since 2.0
+ */
+public interface CompilationResult {
+
+    /**
+     * Return a list of error messages that occured during
+     * compilation. If no errors occured <code>null</code>
+     * is returned.
+     * @return A list of error messages or <code>null</code>.
+     */
+    List<CompilerMessage> getErrors();
+
+    /**
+     * Return a list of warnings that occured during
+     * compilation. If no warnings occured <code>null</code>
+     * is returned.
+     * @return A list of warnings or <code>null</code>.
+     */
+    List<CompilerMessage> getWarnings();
+
+    /**
+     * Was a compilation required or were all classes recent?
+     * @return <code>true>/code> if classes were compiled.
+     */
+    boolean didCompile();
+
+    /**
+     * Try to load the compiled class.
+     * The class loading might fail if the className is not
+     * one of the compiled sources, if the compilation failed
+     * or if a class loader writer has been used in combination
+     * with a class loader that is not able to load the classes
+     * written by the class loader writer.
+     * @return The compiled class
+     * @throws ClassNotFoundException If the class could not be found
+     *         or compilation failed.
+     */
+    Class<?> loadCompiledClass(final String className)
+    throws ClassNotFoundException;
+}
diff --git a/src/main/java/org/apache/sling/commons/compiler/CompilationUnit.java b/src/main/java/org/apache/sling/commons/compiler/CompilationUnit.java
new file mode 100644
index 0000000..415c237
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/compiler/CompilationUnit.java
@@ -0,0 +1,48 @@
+/*
+ * 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.sling.commons.compiler;
+
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * This interface describes a compilation unit - usually a java class.
+ * @since 2.0
+ */
+public interface CompilationUnit {
+
+    /**
+     * Return an input stream for the contents.
+     * The compiler will close this stream in all cases!
+     */
+    Reader getSource()
+    throws IOException;
+
+    /**
+     * Returns the name of the top level public type.
+     * This name includes the package.
+     * @return the name of the top level public type.
+     */
+    String getMainClassName();
+
+    /**
+     * Return the last modified for the compilation unit.
+     * @return The last modified information or <code>-1</code> if
+     *         the information can't be detected.
+     */
+    long getLastModified();
+}
diff --git a/src/main/java/org/apache/sling/commons/compiler/CompilationUnitWithSource.java b/src/main/java/org/apache/sling/commons/compiler/CompilationUnitWithSource.java
new file mode 100644
index 0000000..9a4921b
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/compiler/CompilationUnitWithSource.java
@@ -0,0 +1,27 @@
+/*
+ * 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.sling.commons.compiler;
+
+/**
+ * Extension of the CompilationUnit interface which allows for the explicit
+ * declaration of the source file name.
+ * @since 2.1
+ */
+public interface CompilationUnitWithSource extends CompilationUnit {
+
+    String getFileName();
+}
diff --git a/src/main/java/org/apache/sling/commons/compiler/CompilerMessage.java b/src/main/java/org/apache/sling/commons/compiler/CompilerMessage.java
new file mode 100644
index 0000000..fd98d7a
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/compiler/CompilerMessage.java
@@ -0,0 +1,101 @@
+/*
+ * 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.sling.commons.compiler;
+
+/**
+ * This class encapsulates a message produced the compiler.
+ * A message is either a warning or an error.
+ * The messages are retrieved from the {@link CompilationResult}.
+ *
+ * @since 2.0
+ */
+public class CompilerMessage {
+
+    /**
+     * The line number of the offending program text
+     */
+    private final int line;
+
+    /**
+     * The column number of the offending program text
+     */
+    private final int column;
+
+    /**
+     * The name of the file containing the offending program text
+     */
+    private final String file;
+
+    /**
+     * The actual text
+     */
+    private final String message;
+
+    /**
+     * The error message constructor.
+     *
+     * @param file The name of the file containing the offending program text
+     * @param line The line number of the offending program text
+     * @param column The column number of the offending program text
+     * @param message The actual text
+     */
+    public CompilerMessage(final String file,
+                           final int line,
+                           final int column,
+                           final String message) {
+        this.file = file;
+        this.line = line;
+        this.column = column;
+        this.message = message;
+    }
+
+    /**
+     * Return the filename associated with this compiler message.
+     *
+     * @return The filename associated with this compiler message
+     */
+    public String getFile() {
+        return file;
+    }
+
+    /**
+     * Return the line number of the program text originating this message
+     *
+     * @return The line number of the program text originating this message
+     */
+    public int getLine() {
+        return this.line;
+    }
+
+    /**
+     * Return the column number of the program text originating this message
+     *
+     * @return The column number of the program text originating this message
+     */
+    public int getColumn() {
+        return this.column;
+    }
+
+    /**
+     * Return the message
+     *
+     * @return The message
+     */
+    public String getMessage() {
+        return message;
+    }
+}
diff --git a/src/main/java/org/apache/sling/commons/compiler/JavaCompiler.java b/src/main/java/org/apache/sling/commons/compiler/JavaCompiler.java
new file mode 100644
index 0000000..8d2d629
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/compiler/JavaCompiler.java
@@ -0,0 +1,40 @@
+/*
+ * 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.sling.commons.compiler;
+
+/**
+ * The <code>JavaCompiler</code> provides platform independant Java
+ * compilation support.
+ */
+public interface JavaCompiler {
+
+    /**
+     * Compile the compilation units.
+     * This method checks if the compilation is necessary by using
+     * last modified check of the source to compile and the class
+     * file (if available).
+     * The compiler compiles all sources if at least one of the
+     * class files is out dated!
+     *
+     * @param units The compilation units.
+     * @param options The compilation options - this object is optional
+     * @return The compilation result with more information.
+     * @since 2.0
+     */
+    CompilationResult compile(CompilationUnit[] units,
+                              Options options);
+}
diff --git a/src/main/java/org/apache/sling/commons/compiler/Options.java b/src/main/java/org/apache/sling/commons/compiler/Options.java
new file mode 100644
index 0000000..302c8c2
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/compiler/Options.java
@@ -0,0 +1,111 @@
+/*
+ * 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.sling.commons.compiler;
+
+import java.util.HashMap;
+
+/**
+ * Options for the compilation process.
+ */
+public class Options extends HashMap<String, Object> {
+
+    private static final long serialVersionUID = 1576005888428747431L;
+
+    /** The key for the source version. */
+    public static final String KEY_SOURCE_VERSION = "sourceVersion";
+
+    /** The key for the target version. */
+    public static final String KEY_TARGET_VERSION = "targetVersion";
+
+    /** The key for the generate debug info flag. */
+    public static final String KEY_GENERATE_DEBUG_INFO = "generateDebugInfo";
+
+    public static final String VERSION_RUNTIME = null;
+    public static final String VERSION_1_1 = "1.1";
+    public static final String VERSION_1_2 = "1.2";
+    public static final String VERSION_1_3 = "1.3";
+    public static final String VERSION_1_4 = "1.4";
+    public static final String VERSION_1_5 = "1.5";
+    public static final String VERSION_1_6 = "1.6";
+    public static final String VERSION_1_7 = "1.7";
+    public static final String VERSION_1_8 = "1.8";
+
+    /** The key for the class loader writer.
+     * By default the registered class loader writer service is used. */
+    public static final String KEY_CLASS_LOADER_WRITER = "classLoaderWriter";
+
+    /**
+     * The key for the class loader.
+     * By default the commons dynamic classloader is used.
+     * This property overrides the classloader and ignores the
+     * {@link #KEY_ADDITIONAL_CLASS_LOADER} completly!
+     */
+    public static final String KEY_CLASS_LOADER = "classLoader";
+
+    /**
+     * The key for the additional class loader.
+     * By default the commons dynamic classloader is used.
+     * If this property is used and the {@link #KEY_CLASS_LOADER}
+     * property is not defined, a classloader with the dynamic
+     * class loader (default) and the class loader specified here
+     * is used.
+     */
+    public static final String KEY_ADDITIONAL_CLASS_LOADER = "classLoader";
+
+    /** The key to force the compilation - even if the class files are more recent.
+     * The value should be of type Boolean. */
+    public static final String KEY_FORCE_COMPILATION = "forceCompilation";
+
+    /** The key to ignore warnings - if this option is turned on, the
+     * resulting compilation result does not get the warnings issued
+     * by the compiler.
+     * The value should be of type Boolean. */
+    public static final String KEY_IGNORE_WARNINGS = "ignoreWarnings";
+
+    /**
+     * Default options with the following presets:
+     * - generate debug info : true
+     */
+    public Options() {
+        this.put(KEY_GENERATE_DEBUG_INFO, true);
+    }
+
+    /**
+     * Create a new options object based on an existing one.
+     */
+    public Options(final Options options) {
+        super(options);
+    }
+
+    public String getSourceVersion() {
+        return (String) this.get(KEY_SOURCE_VERSION);
+    }
+
+    /**
+     * @since 2.0
+     */
+    public String getTargetVersion() {
+        return (String) this.get(KEY_TARGET_VERSION);
+    }
+
+    public boolean isGenerateDebugInfo() {
+        if ( this.get(KEY_GENERATE_DEBUG_INFO) != null ) {
+            return (Boolean) this.get(KEY_GENERATE_DEBUG_INFO);
+        }
+        return false;
+    }
+}
diff --git a/src/main/java/org/apache/sling/commons/compiler/impl/CompilationResultImpl.java b/src/main/java/org/apache/sling/commons/compiler/impl/CompilationResultImpl.java
new file mode 100644
index 0000000..5d878b2
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/compiler/impl/CompilationResultImpl.java
@@ -0,0 +1,114 @@
+/*
+ * 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.sling.commons.compiler.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.sling.commons.classloader.ClassLoaderWriter;
+import org.apache.sling.commons.compiler.CompilationResult;
+import org.apache.sling.commons.compiler.CompilerMessage;
+
+/**
+ * Implementation of the compilation result
+ */
+public class CompilationResultImpl implements CompilationResult {
+
+    private List<CompilerMessage> errors;
+
+    private List<CompilerMessage> warnings;
+
+    private final boolean ignoreWarnings;
+
+    private final boolean compilationRequired;
+
+    private final ClassLoaderWriter classLoaderWriter;
+
+    public CompilationResultImpl(final String errorMessage) {
+        this.ignoreWarnings = true;
+        this.classLoaderWriter = null;
+        this.compilationRequired = false;
+        this.onError(errorMessage, "<General>", 0, 0);
+    }
+
+    public CompilationResultImpl(final ClassLoaderWriter writer) {
+        this.ignoreWarnings = true;
+        this.classLoaderWriter = writer;
+        this.compilationRequired = false;
+    }
+
+    public CompilationResultImpl(final boolean ignoreWarnings,
+                                 final ClassLoaderWriter writer) {
+        this.ignoreWarnings = ignoreWarnings;
+        this.classLoaderWriter = writer;
+        this.compilationRequired = true;
+    }
+
+    /**
+     * @see org.apache.sling.commons.compiler.CompilationResult#getErrors()
+     */
+    public List<CompilerMessage> getErrors() {
+        return this.errors;
+    }
+
+    /**
+     * @see org.apache.sling.commons.compiler.CompilationResult#getWarnings()
+     */
+    public List<CompilerMessage> getWarnings() {
+        return this.warnings;
+    }
+
+    /**
+     * @see org.apache.sling.commons.compiler.CompilationResult#loadCompiledClass(java.lang.String)
+     */
+    public Class<?> loadCompiledClass(final String className)
+    throws ClassNotFoundException {
+        if ( errors != null ) {
+            throw new ClassNotFoundException(className);
+        }
+        return this.classLoaderWriter.getClassLoader().loadClass(className);
+    }
+
+    /**
+     * @see org.apache.sling.commons.compiler.CompilationResult#didCompile()
+     */
+    public boolean didCompile() {
+        return this.compilationRequired;
+    }
+
+    /**
+     * Notification of an error
+     */
+    public void onError(String msg, String sourceFile, int line, int position) {
+        if ( errors == null ) {
+            errors = new ArrayList<CompilerMessage>();
+        }
+        errors.add(new CompilerMessage(sourceFile, line, position, msg));
+    }
+
+    /**
+     * Notification of a warning
+     */
+    public void onWarning(String msg, String sourceFile, int line, int position) {
+        if ( !this.ignoreWarnings ) {
+            if ( warnings == null ) {
+                warnings = new ArrayList<CompilerMessage>();
+            }
+            warnings.add(new CompilerMessage(sourceFile, line, position, msg));
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/commons/compiler/impl/EclipseJavaCompiler.java b/src/main/java/org/apache/sling/commons/compiler/impl/EclipseJavaCompiler.java
new file mode 100644
index 0000000..b969f26
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/compiler/impl/EclipseJavaCompiler.java
@@ -0,0 +1,479 @@
+/*
+ * 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.sling.commons.compiler.impl;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.commons.classloader.ClassLoaderWriter;
+import org.apache.sling.commons.compiler.CompilationResult;
+import org.apache.sling.commons.compiler.CompilationUnit;
+import org.apache.sling.commons.compiler.CompilationUnitWithSource;
+import org.apache.sling.commons.compiler.JavaCompiler;
+import org.apache.sling.commons.compiler.Options;
+import org.eclipse.jdt.core.compiler.CategorizedProblem;
+import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.ClassFile;
+import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
+import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
+import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
+import org.eclipse.jdt.internal.compiler.IProblemFactory;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
+import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
+import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
+import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The <code>EclipseJavaCompiler</code> provides platform independant
+ * Java compilation support using the Eclipse Java Compiler (org.eclipse.jdt).
+ *
+ */
+@Component
+@Service(value=JavaCompiler.class)
+public class EclipseJavaCompiler implements JavaCompiler {
+
+    /** Logger instance */
+    private final Logger logger = LoggerFactory.getLogger(EclipseJavaCompiler.class);
+
+    @Reference
+    private ClassLoaderWriter classLoaderWriter;
+
+    /** the static problem factory */
+    private IProblemFactory problemFactory = new DefaultProblemFactory(Locale.getDefault());
+
+    /** the static policy. */
+    private final IErrorHandlingPolicy policy = DefaultErrorHandlingPolicies.proceedWithAllProblems();
+
+    /**
+     * Get the classloader for the compilation.
+     */
+    private ClassLoader getClassLoader(final Options options, final ClassLoaderWriter classLoaderWriter) {
+        final ClassLoader loader;
+        if ( options.get(Options.KEY_CLASS_LOADER) != null ) {
+            loader = (ClassLoader)options.get(Options.KEY_CLASS_LOADER);
+        } else if ( options.get(Options.KEY_ADDITIONAL_CLASS_LOADER) != null ) {
+            final ClassLoader additionalClassLoader = (ClassLoader)options.get(Options.KEY_ADDITIONAL_CLASS_LOADER);
+            loader = new ClassLoader(classLoaderWriter.getClassLoader()) {
+                @Override
+                protected Class<?> findClass(String name)
+                throws ClassNotFoundException {
+                    return additionalClassLoader.loadClass(name);
+                }
+
+                @Override
+                protected URL findResource(String name) {
+                    return additionalClassLoader.getResource(name);
+                }
+            };
+        } else {
+            final ClassLoader cl = classLoaderWriter.getClassLoader();
+            if ( cl == null ) {
+                loader = this.classLoaderWriter.getClassLoader();
+            } else {
+                loader = cl;
+            }
+        }
+        return loader;
+    }
+
+    /**
+     * Get the class loader writer for the compilation.
+     */
+    private ClassLoaderWriter getClassLoaderWriter(final Options options) {
+        if (options.get(Options.KEY_CLASS_LOADER_WRITER) != null ) {
+            return (ClassLoaderWriter)options.get(Options.KEY_CLASS_LOADER_WRITER);
+        }
+        return this.classLoaderWriter;
+    }
+
+    /**
+     * Check if the compiled class file is older than the source file
+     */
+    private boolean isOutDated(final CompilationUnit unit,
+                               final ClassLoaderWriter writer) {
+        final long targetLastModified = writer.getLastModified('/' + unit.getMainClassName().replace('.', '/') + ".class");
+        if (targetLastModified < 0) {
+            return true;
+        }
+
+        return targetLastModified < unit.getLastModified();
+    }
+
+    /**
+     * Return the force compilation value
+     */
+    private boolean isForceCompilation(final Options options) {
+        final Boolean flag = (Boolean)options.get(Options.KEY_FORCE_COMPILATION);
+        if ( flag != null ) {
+            return flag;
+        }
+        return false;
+    }
+
+    /**
+     * Return the ignore warnings value
+     */
+    private boolean isIgnoreWarnings(final Options options) {
+        final Boolean flag = (Boolean)options.get(Options.KEY_IGNORE_WARNINGS);
+        if ( flag != null ) {
+            return flag;
+        }
+        return false;
+    }
+
+    private static final Options EMPTY_OPTIONS = new Options();
+
+    /**
+     * @see org.apache.sling.commons.compiler.JavaCompiler#compile(org.apache.sling.commons.compiler.CompilationUnit[], org.apache.sling.commons.compiler.Options)
+     */
+    public CompilationResult compile(final CompilationUnit[] units,
+                                     final Options compileOptions) {
+        // make sure we have an options object (to avoid null checks all over the place)
+        final Options options = (compileOptions != null ? compileOptions : EMPTY_OPTIONS);
+
+        // get classloader and classloader writer
+        final ClassLoaderWriter writer = this.getClassLoaderWriter(options);
+        if ( writer == null ) {
+            return new CompilationResultImpl("Class loader writer for compilation is not available.");
+        }
+        final ClassLoader loader = this.getClassLoader(options, writer);
+        if ( loader == null ) {
+            return new CompilationResultImpl("Class loader for compilation is not available.");
+        }
+
+        // check sources for compilation
+        boolean needsCompilation = isForceCompilation(options);
+        if ( !needsCompilation ) {
+            for(final CompilationUnit unit : units) {
+                if ( this.isOutDated(unit, writer) ) {
+                    needsCompilation = true;
+                    break;
+                }
+            }
+        }
+        if ( !needsCompilation ) {
+            logger.debug("All source files are recent - no compilation required.");
+            return new CompilationResultImpl(writer);
+        }
+
+        // delete old class files
+        for(final CompilationUnit unit : units) {
+            final String name = '/' + unit.getMainClassName().replace('.', '/') + ".class";
+            writer.delete(name);
+        }
+
+        // create properties for the settings object
+        final Map<String, String> props = new HashMap<String, String>();
+        if (options.isGenerateDebugInfo()) {
+            props.put(CompilerOptions.OPTION_LocalVariableAttribute, "generate");
+            props.put(CompilerOptions.OPTION_LineNumberAttribute, "generate");
+            props.put(CompilerOptions.OPTION_SourceFileAttribute, "generate");
+        }
+        if (options.getSourceVersion() != null) {
+            props.put(CompilerOptions.OPTION_Source, options.getSourceVersion());
+            props.put(CompilerOptions.OPTION_Compliance, options.getSourceVersion());
+        }
+        if (options.getTargetVersion() != null) {
+            props.put(CompilerOptions.OPTION_TargetPlatform, options.getTargetVersion());
+        }
+        props.put(CompilerOptions.OPTION_Encoding, "UTF8");
+
+        // create the settings
+        final CompilerOptions settings = new CompilerOptions(props);
+        logger.debug("Compiling with settings {}.", settings);
+
+        // create the result
+        final CompilationResultImpl result = new CompilationResultImpl(isIgnoreWarnings(options), writer);
+        // create the context
+        final CompileContext context = new CompileContext(units, result, writer, loader);
+
+        // create the compiler
+        final org.eclipse.jdt.internal.compiler.Compiler compiler =
+                new org.eclipse.jdt.internal.compiler.Compiler(
+                        context,
+                        this.policy,
+                        settings,
+                        context,
+                        this.problemFactory,
+                        null,
+                        null);
+
+        // compile
+        compiler.compile(context.getSourceUnits());
+
+        return result;
+    }
+
+    //--------------------------------------------------------< inner classes >
+
+    private class CompileContext implements ICompilerRequestor, INameEnvironment {
+
+        private final Map<String,ICompilationUnit> compUnits;
+
+        private final CompilationResultImpl errorHandler;
+        private final ClassLoaderWriter classLoaderWriter;
+        private final ClassLoader classLoader;
+
+        public CompileContext(final CompilationUnit[] units,
+         		              final CompilationResultImpl errorHandler,
+        		              final ClassLoaderWriter classWriter,
+        		              final ClassLoader classLoader) {
+        	this.compUnits = new HashMap<String,ICompilationUnit>();
+            for (int i = 0; i < units.length; i++) {
+                CompilationUnitAdapter cua = new CompilationUnitAdapter(units[i], errorHandler);
+                char[][] compoundName = CharOperation.arrayConcat(cua.getPackageName(), cua.getMainTypeName());
+                this.compUnits.put(CharOperation.toString(compoundName), new CompilationUnitAdapter(units[i], errorHandler));
+            }
+
+        	this.errorHandler = errorHandler;
+            this.classLoaderWriter = classWriter;
+            this.classLoader = classLoader;
+        }
+
+        public ICompilationUnit[] getSourceUnits() {
+        	return compUnits.values().toArray(
+        			new ICompilationUnit[compUnits.size()]);
+        }
+
+        /**
+         * @see org.eclipse.jdt.internal.compiler.ICompilerRequestor#acceptResult(org.eclipse.jdt.internal.compiler.CompilationResult)
+         */
+        public void acceptResult(org.eclipse.jdt.internal.compiler.CompilationResult result) {
+            if (result.hasProblems()) {
+                CategorizedProblem[] problems = result.getProblems();
+                for (int i = 0; i < problems.length; i++) {
+                    CategorizedProblem problem = problems[i];
+                    String msg = problem.getMessage();
+                    String fileName = CharOperation.charToString(problem.getOriginatingFileName());
+                    int line = problem.getSourceLineNumber();
+                    int pos = problem.getSourceStart();
+
+                    if (problem.isError()) {
+                        this.errorHandler.onError(msg, fileName, line, pos);
+                    } else if (problem.isWarning()) {
+                        this.errorHandler.onWarning(msg, fileName, line, pos);
+                    } else {
+                        logger.debug("unknown problem category: {}", problem);
+                    }
+                }
+            }
+            ClassFile[] classFiles = result.getClassFiles();
+            for (int i = 0; i < classFiles.length; i++) {
+                ClassFile classFile = classFiles[i];
+                String className = CharOperation.toString(classFile.getCompoundName());
+                try {
+                    this.write(className, classFile.getBytes());
+                } catch (IOException e) {
+                    this.errorHandler.onError("Unable to write class file: " + e.getMessage(), className, 0, 0);
+                }
+            }
+        }
+
+        /**
+         * @see org.eclipse.jdt.internal.compiler.env.INameEnvironment#findType(char[][])
+         */
+        public NameEnvironmentAnswer findType(char[][] compoundTypeName) {
+            // check 1st if type corresponds with any of current compilation units
+            String fqn = CharOperation.toString(compoundTypeName);
+            ICompilationUnit cu = compUnits.get(fqn);
+            if (cu != null) {
+                return new NameEnvironmentAnswer(cu, null);
+            }
+
+            // locate the class through the class loader
+            try {
+                byte[] bytes = this.findClass(CharOperation.toString(compoundTypeName));
+                if (bytes == null) {
+                    return null;
+                }
+                ClassFileReader classFileReader =
+                        new ClassFileReader(bytes, fqn.toCharArray(), true);
+                return new NameEnvironmentAnswer(classFileReader, null);
+            } catch (Exception e) {
+                return null;
+            }
+        }
+
+        /**
+         * @see org.eclipse.jdt.internal.compiler.env.INameEnvironment#findType(char[], char[][])
+         */
+        public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) {
+            return findType(CharOperation.arrayConcat(packageName, typeName));
+        }
+
+        /**
+         * @see org.eclipse.jdt.internal.compiler.env.INameEnvironment#isPackage(char[][], char[])
+         */
+        public boolean isPackage(char[][] parentPackageName, char[] packageName) {
+            String fqn = CharOperation.toString(
+                    CharOperation.arrayConcat(parentPackageName, packageName));
+            return compUnits.get(fqn) == null && this.isPackage(fqn);
+        }
+
+        /**
+         * @see org.eclipse.jdt.internal.compiler.env.INameEnvironment#cleanup()
+         */
+        public void cleanup() {
+            // nothing to do
+        }
+
+        /**
+         * Write the classfile
+         */
+        private void write(String name, byte[] data) throws IOException {
+            final OutputStream os = this.classLoaderWriter.getOutputStream('/' + name.replace('.', '/') + ".class");
+            os.write(data);
+            os.close();
+        }
+
+        private boolean isPackage(String result) {
+            String resourceName = result.replace('.', '/') + ".class";
+            if ( resourceName.startsWith("/") ) {
+                resourceName = resourceName.substring(1);
+            }
+            final InputStream is = this.classLoader.getResourceAsStream(resourceName);
+            if ( is != null ) {
+                try {
+                    is.close();
+                } catch (IOException ignore) {}
+            }
+            return is == null;
+        }
+
+        private byte[] findClass(String name) throws Exception {
+            final String resourceName = name.replace('.', '/') + ".class";
+            final InputStream is = this.classLoader.getResourceAsStream(resourceName);
+            if (is != null) {
+                try {
+                    byte[] buf = new byte[8192];
+                    ByteArrayOutputStream baos = new ByteArrayOutputStream(buf.length);
+                    int count;
+                    while ((count = is.read(buf, 0, buf.length)) > 0) {
+                        baos.write(buf, 0, count);
+                    }
+                    baos.flush();
+                    return baos.toByteArray();
+                } finally {
+                    try {
+                        is.close();
+                    } catch (IOException ignore) {}
+                }
+            }
+            return null;
+        }
+    }
+
+    private class CompilationUnitAdapter implements ICompilationUnit {
+
+        private final CompilationResultImpl errorHandler;
+        private final CompilationUnit compUnit;
+        private final String mainTypeName;
+        private final String packageName;
+
+        public CompilationUnitAdapter(final CompilationUnit compUnit, final CompilationResultImpl errorHandler) {
+            this.compUnit = compUnit;
+            this.errorHandler = errorHandler;
+            final int pos = compUnit.getMainClassName().lastIndexOf('.');
+            if ( pos == -1 ) {
+                this.packageName = "";
+                this.mainTypeName = compUnit.getMainClassName();
+            } else {
+                this.packageName = compUnit.getMainClassName().substring(0, pos);
+                this.mainTypeName = compUnit.getMainClassName().substring(pos + 1);
+            }
+        }
+
+        /**
+         * @see org.eclipse.jdt.internal.compiler.env.ICompilationUnit#getContents()
+         */
+        public char[] getContents() {
+            Reader fr = null;
+            try {
+                fr = this.compUnit.getSource();
+                final Reader reader = new BufferedReader(fr);
+                try {
+                    char[] chars = new char[8192];
+                    StringBuilder buf = new StringBuilder();
+                    int count;
+                    while ((count = reader.read(chars, 0, chars.length)) > 0) {
+                        buf.append(chars, 0, count);
+                    }
+                    final char[] result = new char[buf.length()];
+                    buf.getChars(0, result.length, result, 0);
+                    return result;
+                } finally {
+                    reader.close();
+                }
+            } catch (IOException e) {
+                this.errorHandler.onError("Unable to read source file " + this.compUnit.getMainClassName() + " : " + e.getMessage(),
+                        this.compUnit.getMainClassName(), 0, 0);
+                return null;
+            } finally {
+                if ( fr != null ) {
+                    try { fr.close(); } catch (IOException ignore) {}
+                }
+            }
+        }
+
+        /**
+         * @see org.eclipse.jdt.internal.compiler.env.ICompilationUnit#getMainTypeName()
+         */
+        public char[] getMainTypeName() {
+            return this.mainTypeName.toCharArray();
+        }
+
+        /**
+         * @see org.eclipse.jdt.internal.compiler.env.ICompilationUnit#getPackageName()
+         */
+        public char[][] getPackageName() {
+            return CharOperation.splitOn('.', this.packageName.toCharArray());
+        }
+
+        /**
+         * @see org.eclipse.jdt.internal.compiler.env.IDependent#getFileName()
+         */
+        public char[] getFileName() {
+            if (compUnit instanceof CompilationUnitWithSource) {
+                return ((CompilationUnitWithSource)compUnit).getFileName().toCharArray();
+            } else {
+                return (this.packageName.replace('.', '/') + '/' + this.mainTypeName + ".java").toCharArray();
+            }
+        }
+
+        /**
+         * @see org.eclipse.jdt.internal.compiler.env.ICompilationUnit#ignoreOptionalProblems()
+         */
+        public boolean ignoreOptionalProblems() {
+            return false;
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/commons/compiler/impl/IsolatedClassLoader.java b/src/main/java/org/apache/sling/commons/compiler/impl/IsolatedClassLoader.java
new file mode 100644
index 0000000..ace970a
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/compiler/impl/IsolatedClassLoader.java
@@ -0,0 +1,162 @@
+/*
+ * 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.sling.commons.compiler.impl;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.AccessController;
+import java.security.PrivilegedExceptionAction;
+import java.security.SecureClassLoader;
+
+import org.apache.sling.commons.classloader.ClassLoaderWriter;
+
+
+/**
+ * The <code>IsolatedClassLoader</code> class loads classes through
+ * the class loader writer
+ */
+public final class IsolatedClassLoader
+    extends SecureClassLoader {
+
+    private final ClassLoaderWriter classLoaderWriter;
+
+    public IsolatedClassLoader(final ClassLoader parent,
+            final ClassLoaderWriter classLoaderWriter) {
+        super(parent);
+        this.classLoaderWriter = classLoaderWriter;
+    }
+
+
+    //---------- Class loader overwrites -------------------------------------
+
+    /**
+     * Loads the class from this <code>ClassLoader</class>.  If the
+     * class does not exist in this one, we check the parent.  Please
+     * note that this is the exact opposite of the
+     * <code>ClassLoader</code> spec.  We use it to work around
+     * inconsistent class loaders from third party vendors.
+     *
+     * @param     name the name of the class
+     * @param     resolve if <code>true</code> then resolve the class
+     * @return    the resulting <code>Class</code> object
+     * @exception ClassNotFoundException if the class could not be found
+     */
+    public final Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+        // First check if it's already loaded
+        Class<?> clazz = findLoadedClass(name);
+
+        if (clazz == null) {
+
+            try {
+                clazz = findClass(name);
+            } catch (final ClassNotFoundException cnfe) {
+                final ClassLoader parent = getParent();
+                if (parent != null) {
+                    // Ask to parent ClassLoader (can also throw a CNFE).
+                    clazz = parent.loadClass(name);
+                } else {
+                    // Propagate exception
+                    throw cnfe;
+                }
+            }
+        }
+
+        if (resolve) {
+            resolveClass(clazz);
+        }
+
+        return clazz;
+    }
+
+    /**
+     * Finds and loads the class with the specified name from the class path.
+     *
+     * @param name the name of the class
+     * @return the resulting class
+     *
+     * @throws ClassNotFoundException If the named class could not be found or
+     *      if this class loader has already been destroyed.
+     */
+    protected Class<?> findClass(final String name) throws ClassNotFoundException {
+        try {
+            return AccessController.doPrivileged(
+                new PrivilegedExceptionAction<Class<?>>() {
+
+                    public Class<?> run() throws ClassNotFoundException {
+                        return findClassPrivileged(name);
+                    }
+                });
+        } catch (final java.security.PrivilegedActionException pae) {
+            throw (ClassNotFoundException) pae.getException();
+        }
+    }
+
+    /**
+     * Tries to find the class in the class path from within a
+     * <code>PrivilegedAction</code>. Throws <code>ClassNotFoundException</code>
+     * if no class can be found for the name.
+     *
+     * @param name the name of the class
+     *
+     * @return the resulting class
+     *
+     * @throws ClassNotFoundException if the class could not be found
+     * @throws NullPointerException If this class loader has already been
+     *      destroyed.
+     */
+    private Class<?> findClassPrivileged(final String name) throws ClassNotFoundException {
+        // prepare the name of the class
+        final String path = "/" + name.replace('.', '/') + ".class";
+        InputStream is = null;
+        try {
+            is = this.classLoaderWriter.getInputStream(path);
+            final Class<?> c = defineClass(name, is);
+            if (c == null) {
+                throw new ClassNotFoundException(name);
+            }
+            return c;
+        } catch ( final ClassNotFoundException cnfe) {
+            throw cnfe;
+        } catch (final Throwable t) {
+            throw new ClassNotFoundException(name, t);
+        }
+     }
+
+    /**
+     * Defines a class getting the bytes for the class from the resource
+     *
+     * @param name The fully qualified class name
+     * @param is The resource to obtain the class bytes from
+     *
+     * @throws IOException If a problem occurrs reading the class bytes from
+     *      the resource.
+     * @throws ClassFormatError If the class bytes read from the resource are
+     *      not a valid class.
+     */
+    private Class<?> defineClass(final String name, final InputStream is)
+    throws IOException {
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        final byte[] buffer = new byte[2048];
+        int l;
+        while ( ( l = is.read(buffer)) >= 0 ) {
+            baos.write(buffer, 0, l);
+        }
+        final byte[] data = baos.toByteArray();
+        return defineClass(name, data, 0, data.length);
+    }
+}
diff --git a/src/test/java/org/apache/sling/commons/compiler/impl/CompilerJava5Test.java b/src/test/java/org/apache/sling/commons/compiler/impl/CompilerJava5Test.java
new file mode 100644
index 0000000..ac5f0b5
--- /dev/null
+++ b/src/test/java/org/apache/sling/commons/compiler/impl/CompilerJava5Test.java
@@ -0,0 +1,123 @@
+/*
+ * 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.sling.commons.compiler.impl;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+
+import junit.framework.TestCase;
+
+import org.apache.sling.commons.classloader.ClassLoaderWriter;
+import org.apache.sling.commons.compiler.CompilationResult;
+import org.apache.sling.commons.compiler.CompilationUnit;
+import org.apache.sling.commons.compiler.Options;
+
+/**
+ * Test case for java 5 support
+ */
+public class CompilerJava5Test extends TestCase
+        implements ClassLoaderWriter {
+
+    public void testJava5Support() throws Exception {
+        String sourceFile = "Java5Test";
+
+        CompilationUnit unit = createCompileUnit(sourceFile);
+        final Options options = new Options();
+        options.put(Options.KEY_SOURCE_VERSION, Options.VERSION_1_5);
+        options.put(Options.KEY_CLASS_LOADER_WRITER, this);
+        options.put(Options.KEY_CLASS_LOADER, this.getClass().getClassLoader());
+
+        final CompilationResult result = new EclipseJavaCompiler().compile(new CompilationUnit[]{unit}, options);
+        assertNotNull(result);
+        assertNull(result.getErrors());
+    }
+
+    //--------------------------------------------------------< misc. helpers >
+
+    private CompilationUnit createCompileUnit(final String sourceFile) throws Exception {
+        return new CompilationUnit() {
+
+            /**
+             * @see org.apache.sling.commons.compiler.CompilationUnit#getMainClassName()
+             */
+            public String getMainClassName() {
+                return "org.apache.sling.commons.compiler.test." + sourceFile;
+            }
+
+            /**
+             * @see org.apache.sling.commons.compiler.CompilationUnit#getSource()
+             */
+            public Reader getSource() throws IOException {
+                InputStream in = getClass().getClassLoader().getResourceAsStream(sourceFile);
+                return new InputStreamReader(in, "UTF-8");
+            }
+
+            /**
+             * @see org.apache.sling.commons.compiler.CompilationUnit#getLastModified()
+             */
+            public long getLastModified() {
+                return 0;
+            }
+        };
+    }
+
+    /**
+     * @see org.apache.sling.commons.classloader.ClassLoaderWriter#delete(java.lang.String)
+     */
+    public boolean delete(String path) {
+        return false;
+    }
+
+    /**
+     * @see org.apache.sling.commons.classloader.ClassLoaderWriter#getInputStream(java.lang.String)
+     */
+    public InputStream getInputStream(String path) throws IOException {
+        return null;
+    }
+
+    /**
+     * @see org.apache.sling.commons.classloader.ClassLoaderWriter#getLastModified(java.lang.String)
+     */
+    public long getLastModified(String path) {
+        return -1;
+    }
+
+    /**
+     * @see org.apache.sling.commons.classloader.ClassLoaderWriter#getOutputStream(java.lang.String)
+     */
+    public OutputStream getOutputStream(String path) {
+        return new ByteArrayOutputStream();
+    }
+
+    /**
+     * @see org.apache.sling.commons.classloader.ClassLoaderWriter#rename(java.lang.String, java.lang.String)
+     */
+    public boolean rename(String oldPath, String newPath) {
+        return false;
+    }
+
+    /**
+     * @see org.apache.sling.commons.classloader.ClassLoaderWriter#getClassLoader()
+     */
+    public ClassLoader getClassLoader() {
+        return null;
+    }
+}
diff --git a/src/test/resources/Java5Test b/src/test/resources/Java5Test
new file mode 100644
index 0000000..44658bc
--- /dev/null
+++ b/src/test/resources/Java5Test
@@ -0,0 +1,31 @@
+/*
+ * 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.sling.commons.compiler.test;
+
+import java.util.List;
+
+/**
+ * Class to test to compile Java 5 specific code
+ */
+public class Java5Test {
+    public int sum(List<Integer> intList) {
+	int result = 0;
+	for(int i=0;i<intList.size();i++)
+	    result += intList.get(i).intValue();
+	return result;
+    }
+}