You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 10:03:35 UTC

[sling-org-apache-sling-scripting-java] 01/41: SLING-619 Add a scripting engine for java servlets.

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.scripting.java-1.0.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-scripting-java.git

commit b709f596557bf0799f3220c7bb761a95ca57da5d
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Wed Aug 20 15:22:10 2008 +0000

    SLING-619 Add a scripting engine for java servlets.
    
    git-svn-id: https://svn.apache.org/repos/asf/incubator/sling/trunk/scripting/java@687373 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml                                            | 139 ++++++++
 .../sling/scripting/java/CompilationContext.java   | 232 +++++++++++++
 .../apache/sling/scripting/java/CompilerError.java | 161 +++++++++
 .../apache/sling/scripting/java/CompilerUtil.java  | 129 +++++++
 .../scripting/java/JavaScriptEngineFactory.java    | 271 +++++++++++++++
 .../sling/scripting/java/JavaServletConfig.java    |  94 ++++++
 .../sling/scripting/java/JavaServletContext.java   | 285 ++++++++++++++++
 .../org/apache/sling/scripting/java/Options.java   | 177 ++++++++++
 .../apache/sling/scripting/java/ServletCache.java  |  72 ++++
 .../sling/scripting/java/ServletWrapper.java       | 275 +++++++++++++++
 .../sling/scripting/java/SlingIOProvider.java      | 369 +++++++++++++++++++++
 .../sling/scripting/java/jdt/CompilationUnit.java  | 282 ++++++++++++++++
 .../scripting/java/jdt/EclipseJavaCompiler.java    | 118 +++++++
 13 files changed, 2604 insertions(+)

diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..c2894dd
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,139 @@
+<?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>4-incubator-SNAPSHOT</version>
+        <relativePath>../../parent/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>org.apache.sling.scripting.java</artifactId>
+    <version>2.0.0-incubator-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+
+    <name>Sling - Scripting - Java Support</name>
+    <description>Support for scripting Java</description>
+
+    <scm>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/incubator/sling/trunk/scripting/java</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/incubator/sling/trunk/scripting/java</developerConnection>
+        <url>http://svn.apache.org/viewvc/incubator/sling/trunk/scripting/java</url>
+    </scm>
+
+    <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.*, *
+                        </Import-Package>
+                        <Private-Package>
+                            org.apache.sling.scripting.java,
+                            org.apache.sling.scripting.java.jdt,
+                            org.eclipse.jdt.*
+                        </Private-Package>
+                        <DynamicImport-Package>*</DynamicImport-Package>
+
+                        <ScriptEngine-Name>${pom.name}</ScriptEngine-Name>
+                        <ScriptEngine-Version>${pom.version}</ScriptEngine-Version>
+
+                        <Embed-Dependency>
+                            jasper*
+                        </Embed-Dependency>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <reporting>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <configuration>
+                  <!-- No javadocs -->
+                    <excludePackageNames>
+                        org.apache.sling.scripting
+                    </excludePackageNames>
+                </configuration>
+            </plugin>
+        </plugins>
+    </reporting>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.api</artifactId>
+            <version>2.0.2-incubator</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.scripting.api</artifactId>
+            <version>2.0.2-incubator</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.jcr.api</artifactId>
+            <version>2.0.2-incubator</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.jcr.classloader</artifactId>
+            <version>2.0.2-incubator</version>
+        </dependency>
+
+     <!-- We use the same eclipse jdt as the jsp bundle -->
+        <dependency>
+            <groupId>org.apache.tomcat</groupId>
+            <artifactId>jasper-jdt</artifactId>
+            <version>6.0.18</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.5</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/scripting/java/CompilationContext.java b/src/main/java/org/apache/sling/scripting/java/CompilationContext.java
new file mode 100644
index 0000000..8c5e41b
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/java/CompilationContext.java
@@ -0,0 +1,232 @@
+/*
+ * 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.scripting.java;
+
+import java.io.FileNotFoundException;
+import java.util.List;
+
+import javax.servlet.ServletException;
+
+import org.apache.sling.scripting.java.jdt.EclipseJavaCompiler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ */
+public class CompilationContext {
+
+    private Logger logger = LoggerFactory.getLogger(getClass());
+
+    /** The name of the generated class. */
+    private final String className;
+
+    /** The path to the servlet. */
+    private final String sourcePath;
+
+    /** The mapped path. */
+    private final String mappedSourcePath;
+
+    /** Compilation options. */
+    private final Options options;
+
+    /** The compiler instance. */
+    private final EclipseJavaCompiler compiler;
+
+    /** Sling IO Provider. */
+    private final SlingIOProvider ioProvider;
+
+    private ServletCache servletCache;
+
+    private long lastModificationTest = 0L;
+    private int removed = 0;
+
+    private Class servletClass;
+
+    private final ServletWrapper wrapper;
+
+    /**
+     * A new compilation context.
+     * @param sourcePath The path to the servlet source.
+     * @param options The compiler options
+     * @param provider The Sling IO Provider
+     * @param servletCache
+     */
+    public CompilationContext(final String sourcePath,
+                              final Options options,
+                              final SlingIOProvider provider,
+                              ServletCache servletCache,
+                              final ServletWrapper wrapper) {
+        this.sourcePath = sourcePath;
+        this.mappedSourcePath = CompilerUtil.mapSourcePath(this.sourcePath);
+        this.className = CompilerUtil.makeClassPath(this.mappedSourcePath);
+
+        this.options = options;
+        this.ioProvider = provider;
+        this.compiler = new EclipseJavaCompiler(this);
+
+        this.servletCache = servletCache;
+        this.wrapper = wrapper;
+    }
+
+    /**
+     * Options
+     */
+    public Options getCompilerOptions() {
+        return options;
+    }
+
+    /**
+     * Provider
+     */
+    public SlingIOProvider getIOProvider() {
+        return this.ioProvider;
+    }
+
+    /**
+     * Return the path to the java servlet source file
+     * @return The source file path.
+     */
+    public String getSourcePath() {
+        return this.sourcePath;
+    }
+
+    public String getJavaClassName() {
+        return this.mappedSourcePath.replace('/', '.');
+    }
+
+    /**
+     * Return the path to the generated class file.
+     * @return The class file path.
+     */
+    public String getClassFilePath() {
+        return this.className;
+    }
+
+    public void incrementRemoved() {
+        if (removed == 0 && servletCache != null) {
+            servletCache.removeWrapper(sourcePath);
+        }
+        removed++;
+    }
+
+    public boolean isRemoved() {
+        if (removed > 1 ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Check if the compiled class file is older than the source file
+     */
+    public boolean isOutDated() {
+        if (this.options.getModificationTestInterval() > 0) {
+
+            if (this.lastModificationTest
+                + (this.options.getModificationTestInterval() * 1000) > System.currentTimeMillis()) {
+                return false;
+            }
+            this.lastModificationTest = System.currentTimeMillis();
+        }
+
+        final long sourceLastModified = this.ioProvider.lastModified(getSourcePath());
+
+        final long targetLastModified = this.ioProvider.lastModified(getCompleteClassPath());
+        if (targetLastModified < 0) {
+            return true;
+        }
+
+        if (targetLastModified < sourceLastModified) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Compiler: outdated: " + getClassFilePath() + " "
+                        + targetLastModified);
+            }
+            return true;
+        }
+
+        return false;
+
+    }
+
+    private String getCompleteClassPath() {
+        return options.getDestinationPath() + getClassFilePath() + ".class";
+    }
+
+    // ==================== Compile and reload ====================
+
+    public void compile() throws ServletException, FileNotFoundException {
+        if (this.isOutDated()) {
+            try {
+                final List<CompilerError> errors = this.compiler.compile();
+                if ( errors != null ) {
+                    //this.ioProvider.delete(getCompleteClassPath());
+                    throw CompilerException.create(errors);
+                }
+                this.wrapper.setReload(true);
+                this.wrapper.setCompilationException(null);
+            } catch (ServletException se) {
+                this.wrapper.setCompilationException(se);
+                throw se;
+            } catch (Exception ex) {
+                final ServletException se = new ServletException("Unable to compile servlet.", ex);
+                // Cache compilation exception
+                this.wrapper.setCompilationException(se);
+                throw se;
+            }
+        }
+    }
+
+    /**
+     * Load the class.
+     */
+    public Class load()
+    throws ServletException, FileNotFoundException {
+        try {
+            servletClass = this.options.getClassLoader().loadClass(this.getClassFilePath().substring(1).replace('/', '.'));
+        } catch (ClassNotFoundException cex) {
+            throw new ServletException("Unable to load servlet class.", cex);
+        } catch (Exception ex) {
+            throw new ServletException("Unable to compile servlet.", ex);
+        }
+        removed = 0;
+        return servletClass;
+    }
+
+    protected final static class CompilerException extends ServletException {
+
+        public static CompilerException create(List<CompilerError> errors) {
+            final StringBuffer buffer = new StringBuffer();
+            buffer.append("Compilation errors:\n");
+            for(final CompilerError e : errors) {
+                buffer.append(e.getFile());
+                buffer.append(", line ");
+                buffer.append(e.getStartLine());
+                buffer.append(", column ");
+                buffer.append(e.getStartColumn());
+                buffer.append(" : " );
+                buffer.append(e.getMessage());
+                buffer.append("\n");
+            }
+            return new CompilerException(buffer.toString());
+        }
+
+        public CompilerException(final String message) {
+           super(message);
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/scripting/java/CompilerError.java b/src/main/java/org/apache/sling/scripting/java/CompilerError.java
new file mode 100644
index 0000000..e29f67c
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/java/CompilerError.java
@@ -0,0 +1,161 @@
+/*
+ * 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.scripting.java;
+
+/**
+ * This class encapsulates an error message produced by a programming language
+ * processor (whether interpreted or compiled)
+ * @version $Id$
+ * @since 2.0
+ */
+
+public class CompilerError {
+  /**
+   * Is this a severe error or a warning?
+   */
+  private boolean error;
+  /**
+   * The start line number of the offending program text
+   */
+  private int startline;
+  /**
+   * The start column number of the offending program text
+   */
+  private int startcolumn;
+  /**
+   * The end line number of the offending program text
+   */
+  private int endline;
+  /**
+   * The end column number of the offending program text
+   */
+  private int endcolumn;
+  /**
+   * The name of the file containing the offending program text
+   */
+  private String file;
+  /**
+   * The actual error text produced by the language processor
+   */
+  private String message;
+
+  /**
+   * The error message constructor.
+   *
+   * @param file The name of the file containing the offending program text
+   * @param error The actual error text produced by the language processor
+   * @param startline The start line number of the offending program text
+   * @param startcolumn The start column number of the offending program text
+   * @param endline The end line number of the offending program text
+   * @param endcolumn The end column number of the offending program text
+   * @param message The actual error text produced by the language processor
+   */
+  public CompilerError(
+    String file,
+    boolean error,
+    int startline,
+    int startcolumn,
+    int endline,
+    int endcolumn,
+    String message
+  )
+  {
+    this.file = file;
+    this.error = error;
+    this.startline = startline;
+    this.startcolumn = startcolumn;
+    this.endline = endline;
+    this.endcolumn = endcolumn;
+    this.message = message;
+  }
+
+  /**
+   * The error message constructor.
+   *
+   * @param message The actual error text produced by the language processor
+   */
+  public CompilerError(String message) {
+    this.message = message;
+  }
+
+  /**
+   * Return the filename associated with this compiler error.
+   *
+   * @return The filename associated with this compiler error
+   */
+  public String getFile() {
+    return file;
+  }
+
+  /**
+   * Assert whether this is a severe error or a warning
+   *
+   * @return Whether the error is severe
+   */
+  public boolean isError() {
+    return error;
+  }
+
+  /**
+   * Return the starting line number of the program text originating this error
+   *
+   * @return The starting line number of the program text originating this error
+   */
+  public int getStartLine() {
+    return startline;
+  }
+
+  /**
+   * Return the starting column number of the program text originating this
+   * error
+   *
+   * @return The starting column number of the program text originating this
+   * error
+   */
+  public int getStartColumn() {
+    return startcolumn;
+  }
+
+  /**
+   * Return the ending line number of the program text originating this error
+   *
+   * @return The ending line number of the program text originating this error
+   */
+  public int getEndLine() {
+    return endline;
+  }
+
+  /**
+   * Return the ending column number of the program text originating this
+   * error
+   *
+   * @return The ending column number of the program text originating this
+   * error
+   */
+  public int getEndColumn() {
+    return endcolumn;
+  }
+
+  /**
+   * Return the message produced by the language processor
+   *
+   * @return The message produced by the language processor
+   */
+  public String getMessage() {
+    return message;
+  }
+}
diff --git a/src/main/java/org/apache/sling/scripting/java/CompilerUtil.java b/src/main/java/org/apache/sling/scripting/java/CompilerUtil.java
new file mode 100644
index 0000000..d415ed7
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/java/CompilerUtil.java
@@ -0,0 +1,129 @@
+/*
+ * 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.scripting.java;
+
+public class CompilerUtil {
+
+    /**
+     * The path has to be mapped to form a proper java class name.
+     * @param path
+     * @return The mapped source path.
+     */
+    public static String mapSourcePath(final String path) {
+        final String str;
+        if ( path.endsWith(".java") ) {
+            str = path.substring(0, path.length() - 5);
+        } else {
+            str = path;
+        }
+        final int pos = str.lastIndexOf("/");
+        if ( pos == -1 ) {
+            return makeJavaIdentifier(str);
+        }
+        return str.substring(0, pos + 1) + makeJavaIdentifier(str.substring(pos + 1));
+    }
+
+    /**
+     * Create the class name from the source name
+     * @param sourcePath The source path
+     * @return The corresponding class file path.
+     */
+    public static String makeClassPath(String sourcePath) {
+        String str = sourcePath;
+        if (str.endsWith(".java")) {
+            str = str.substring(0, str.length() - 5);
+        }
+        return str;
+    }
+
+
+    /**
+     * Converts the given identifier to a legal Java identifier
+     *
+     * @param identifier Identifier to convert
+     *
+     * @return Legal Java identifier corresponding to the given identifier
+     */
+    public static final String makeJavaIdentifier(String identifier) {
+        StringBuffer modifiedIdentifier =
+            new StringBuffer(identifier.length());
+        if (!Character.isJavaIdentifierStart(identifier.charAt(0))) {
+            modifiedIdentifier.append('_');
+        }
+        for (int i = 0; i < identifier.length(); i++) {
+            char ch = identifier.charAt(i);
+            if (Character.isJavaIdentifierPart(ch) && ch != '_') {
+                modifiedIdentifier.append(ch);
+            } else if (ch == '.') {
+                modifiedIdentifier.append('_');
+            } else {
+                modifiedIdentifier.append(mangleChar(ch));
+            }
+        }
+        if (isJavaKeyword(modifiedIdentifier.toString())) {
+            modifiedIdentifier.append('_');
+        }
+        return modifiedIdentifier.toString();
+    }
+
+    /**
+     * Mangle the specified character to create a legal Java class name.
+     */
+    public static final String mangleChar(char ch) {
+        char[] result = new char[5];
+        result[0] = '_';
+        result[1] = Character.forDigit((ch >> 12) & 0xf, 16);
+        result[2] = Character.forDigit((ch >> 8) & 0xf, 16);
+        result[3] = Character.forDigit((ch >> 4) & 0xf, 16);
+        result[4] = Character.forDigit(ch & 0xf, 16);
+        return new String(result);
+    }
+
+    private static final String javaKeywords[] = {
+        "abstract", "assert", "boolean", "break", "byte", "case",
+        "catch", "char", "class", "const", "continue",
+        "default", "do", "double", "else", "enum", "extends",
+        "final", "finally", "float", "for", "goto",
+        "if", "implements", "import", "instanceof", "int",
+        "interface", "long", "native", "new", "package",
+        "private", "protected", "public", "return", "short",
+        "static", "strictfp", "super", "switch", "synchronized",
+        "this", "throws", "transient", "try", "void",
+        "volatile", "while" };
+
+    /**
+     * Test whether the argument is a Java keyword
+     */
+    public static boolean isJavaKeyword(String key) {
+        int i = 0;
+        int j = javaKeywords.length;
+        while (i < j) {
+            int k = (i+j)/2;
+            int result = javaKeywords[k].compareTo(key);
+            if (result == 0) {
+                return true;
+            }
+            if (result < 0) {
+                i = k+1;
+            } else {
+                j = k;
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/scripting/java/JavaScriptEngineFactory.java b/src/main/java/org/apache/sling/scripting/java/JavaScriptEngineFactory.java
new file mode 100644
index 0000000..70190f2
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/java/JavaScriptEngineFactory.java
@@ -0,0 +1,271 @@
+/*
+ * 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.scripting.java;
+
+import static org.apache.sling.api.scripting.SlingBindings.SLING;
+
+import java.io.Reader;
+
+import javax.jcr.RepositoryException;
+import javax.script.Bindings;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptException;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+
+import org.apache.sling.api.SlingException;
+import org.apache.sling.api.SlingIOException;
+import org.apache.sling.api.SlingServletException;
+import org.apache.sling.api.scripting.SlingBindings;
+import org.apache.sling.api.scripting.SlingScript;
+import org.apache.sling.api.scripting.SlingScriptHelper;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.jcr.classloader.RepositoryClassLoaderProvider;
+import org.apache.sling.scripting.api.AbstractScriptEngineFactory;
+import org.apache.sling.scripting.api.AbstractSlingScriptEngine;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The Java engine
+ *
+ * @scr.component label="%javahandler.name" description="%javahandler.description"
+ * @scr.property name="service.description" value="Java Servlet Script Handler"
+ * @scr.property name="service.vendor" value="The Apache Software Foundation"
+ * @scr.service
+ *
+ * @scr.property name="java.javaEncoding" value="UTF-8"
+ * @scr.property name="java.compilerSourceVM" value="1.5"
+ * @scr.property name="java.compilerTargetVM" value="1.5"
+ * @scr.property name="java.development" value="true"
+ * @scr.property name="java.outputPath" value="/var/classes"
+ * @scr.property name="java.modificationTestInterval" value="-1"
+ * @scr.property name="java.classdebuginfo" value="truie"
+ */
+public class JavaScriptEngineFactory extends AbstractScriptEngineFactory {
+
+    private static final String CLASSLOADER_NAME = "admin";
+
+    /** default logger */
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    /** @scr.reference */
+    private SlingRepository repository;
+
+    /**
+     * @scr.reference
+     */
+    private RepositoryClassLoaderProvider repoCLProvider;
+
+    /**
+     * The class loader
+     */
+    private ClassLoader javaClassLoader;
+
+    /** @scr.reference */
+    private ServletContext slingServletContext;
+
+    private SlingIOProvider ioProvider;
+
+    private JavaServletContext javaServletContext;
+
+    private ServletConfig servletConfig;
+
+    private ServletCache servletCache;
+
+    /** Compiler options. */
+    private Options compilerOptions;
+
+    public static final String SCRIPT_TYPE = "java";
+
+    /**
+     * Constructor
+     */
+    public JavaScriptEngineFactory() {
+        setExtensions(SCRIPT_TYPE);
+    }
+
+    /**
+     * @see javax.script.ScriptEngineFactory#getScriptEngine()
+     */
+    public ScriptEngine getScriptEngine() {
+        return new JavaScriptEngine(this);
+    }
+
+    /**
+     * @see javax.script.ScriptEngineFactory#getLanguageName()
+     */
+    public String getLanguageName() {
+        return "Java Servlet Compiler";
+    }
+
+    /**
+     * @see javax.script.ScriptEngineFactory#getLanguageVersion()
+     */
+    public String getLanguageVersion() {
+        return "1.5";
+    }
+
+    /**
+     * Activate this engine
+     * @param componentContext
+     */
+    protected void activate(ComponentContext componentContext) {
+        this.ioProvider = new SlingIOProvider(repository);
+        this.servletCache = new ServletCache();
+
+        this.javaServletContext = new JavaServletContext(ioProvider,
+            slingServletContext);
+
+        this.servletConfig = new JavaServletConfig(javaServletContext,
+            componentContext.getProperties());
+        this.compilerOptions = new Options(componentContext,
+                                           this.javaClassLoader);
+        if (log.isDebugEnabled()) {
+            log.debug("JavaServletScriptEngine.activate()");
+        }
+    }
+
+    /**
+     * Deactivate this engine
+     * @param oldComponentContext
+     */
+    protected void deactivate(ComponentContext oldComponentContext) {
+        if (log.isDebugEnabled()) {
+            log.debug("JavaServletScriptEngine.deactivate()");
+        }
+
+        ioProvider = null;
+        javaServletContext = null;
+        servletConfig = null;
+        servletCache = null;
+        compilerOptions = null;
+    }
+
+    /**
+     * Call the servlet.
+     * @param scriptHelper
+     * @throws SlingServletException
+     * @throws SlingIOException
+     */
+    private void callServlet(Bindings bindings, SlingScriptHelper scriptHelper) {
+
+        ioProvider.setRequestResourceResolver(scriptHelper.getRequest().getResourceResolver());
+        try {
+            ServletWrapper servlet = getWrapperAdapter(scriptHelper);
+            // create a SlingBindings object
+            final SlingBindings slingBindings = new SlingBindings();
+            slingBindings.putAll(bindings);
+            servlet.service(slingBindings);
+        } finally {
+            ioProvider.resetRequestResourceResolver();
+        }
+    }
+
+    private ServletWrapper getWrapperAdapter(
+            SlingScriptHelper scriptHelper) throws SlingException {
+
+        SlingScript script = scriptHelper.getScript();
+        final String scriptName = script.getScriptResource().getPath();
+        ServletWrapper wrapper = this.servletCache.getWrapper(scriptName);
+        if (wrapper != null) {
+            return wrapper;
+        }
+
+        synchronized (this) {
+            wrapper = this.servletCache.getWrapper(scriptName);
+            if (wrapper != null) {
+                return wrapper;
+            }
+
+            wrapper = new ServletWrapper(servletConfig,
+                    this.compilerOptions, ioProvider, scriptName, this.servletCache);
+            this.servletCache.addWrapper(scriptName, wrapper);
+
+            return wrapper;
+        }
+    }
+
+    /**
+     * Bind the class load provider.
+     * @param repositoryClassLoaderProvider the new provider
+     */
+    protected void bindRepositoryClassLoaderProvider(RepositoryClassLoaderProvider rclp) {
+        if ( this.javaClassLoader != null ) {
+            this.ungetClassLoader();
+        }
+        this.getClassLoader(rclp);
+    }
+
+    /**
+     * Unbind the class loader provider.
+     * @param repositoryClassLoaderProvider the old provider
+     */
+    protected void unbindRepositoryClassLoaderProvider(RepositoryClassLoaderProvider rclp) {
+        if ( this.repoCLProvider == rclp ) {
+            this.ungetClassLoader();
+        }
+    }
+
+    /**
+     * Get the class loader
+     */
+    private void getClassLoader(RepositoryClassLoaderProvider rclp) {
+        try {
+            this.repoCLProvider = rclp;
+            this.javaClassLoader = rclp.getClassLoader(CLASSLOADER_NAME);
+        } catch (RepositoryException re) {
+            log.error("Cannot get Java class loader", re);
+        }
+    }
+
+    /**
+     * Unget the class loader
+     */
+    private void ungetClassLoader() {
+        if ( this.repoCLProvider != null ) {
+            if ( this.javaClassLoader != null ) {
+                this.repoCLProvider.ungetClassLoader(this.javaClassLoader);
+                this.javaClassLoader = null;
+            }
+            this.repoCLProvider = null;
+        }
+    }
+    // ---------- Internal -----------------------------------------------------
+
+    private static class JavaScriptEngine extends AbstractSlingScriptEngine {
+
+        JavaScriptEngine(JavaScriptEngineFactory factory) {
+            super(factory);
+        }
+
+        /**
+         * @see javax.script.ScriptEngine#eval(java.io.Reader, javax.script.ScriptContext)
+         */
+        public Object eval(Reader script, ScriptContext context)
+        throws ScriptException {
+            final Bindings props = context.getBindings(ScriptContext.ENGINE_SCOPE);
+            final SlingScriptHelper scriptHelper = (SlingScriptHelper) props.get(SLING);
+            if (scriptHelper != null) {
+                ((JavaScriptEngineFactory)this.getFactory()).callServlet(props, scriptHelper);
+            }
+            return null;
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/scripting/java/JavaServletConfig.java b/src/main/java/org/apache/sling/scripting/java/JavaServletConfig.java
new file mode 100644
index 0000000..48f305e
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/java/JavaServletConfig.java
@@ -0,0 +1,94 @@
+/*
+ * 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.scripting.java;
+
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+
+import org.osgi.framework.Constants;
+
+/**
+ * The <code>JavaServletConfig</code>
+ * is passed to the compiled servlets.
+ */
+public class JavaServletConfig implements ServletConfig {
+
+    /** The servlet context. */
+    private final ServletContext servletContext;
+
+    /** The name of the servlet. */
+    private final String servletName;
+
+    private final Map<String, String> initParams;
+
+    public JavaServletConfig(ServletContext servletContext, Dictionary<?, ?> config) {
+        this.servletContext = servletContext;
+
+        // set the servlet name
+        if (config.get(Constants.SERVICE_DESCRIPTION) == null) {
+            servletName = "Java Script Handler";
+        } else{
+            servletName = config.get(Constants.SERVICE_DESCRIPTION).toString();
+        }
+
+        // copy the "java." properties
+        initParams = new HashMap<String, String>();
+        for (Enumeration<?> ke = config.keys(); ke.hasMoreElements();) {
+            String key = (String) ke.nextElement();
+            if (key.startsWith("java.")) {
+                initParams.put(key.substring("java.".length()),
+                    String.valueOf(config.get(key)));
+            }
+        }
+    }
+
+    /**
+     * @see javax.servlet.ServletConfig#getInitParameter(java.lang.String)
+     */
+    public String getInitParameter(String name) {
+        return initParams.get(name);
+    }
+
+    /**
+     * @see javax.servlet.ServletConfig#getInitParameterNames()
+     */
+    public Enumeration<String> getInitParameterNames() {
+        return Collections.enumeration(initParams.keySet());
+    }
+
+    /**
+     * @see javax.servlet.ServletConfig#getServletContext()
+     */
+    public ServletContext getServletContext() {
+        return servletContext;
+    }
+
+    /**
+     * @see javax.servlet.ServletConfig#getServletName()
+     */
+    public String getServletName() {
+        return servletName;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/scripting/java/JavaServletContext.java b/src/main/java/org/apache/sling/scripting/java/JavaServletContext.java
new file mode 100644
index 0000000..8b1969a
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/java/JavaServletContext.java
@@ -0,0 +1,285 @@
+/*
+ * 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.scripting.java;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Set;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The <code>JavaServletContext</code>
+ * is passed to the compiled servlets.
+ */
+public class JavaServletContext implements ServletContext {
+
+    /** default log */
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private final SlingIOProvider ioProvider;
+    private final ServletContext delegatee;
+
+    JavaServletContext(SlingIOProvider ioProvider, ServletContext componentContext) {
+        this.ioProvider = ioProvider;
+        this.delegatee = componentContext;
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getResource(java.lang.String)
+     */
+    public URL getResource(String path) throws MalformedURLException {
+        if (path.startsWith("/")) {
+            URL url = ioProvider.getURL(path);
+            if (url != null) {
+                return url;
+            }
+        }
+
+        // fall back to trying a real URL
+        return getUrlForResource(path);
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getResourceAsStream(java.lang.String)
+     */
+    public InputStream getResourceAsStream(String path) {
+        // path might be an URL, so only check resource provider in case of an
+        // absolute path - assuming URLs have no leading slash at all, we
+        // don't care for the scheme separating colon here
+        if (path.startsWith("/")) {
+            try {
+                return ioProvider.getInputStream(path);
+            } catch (Exception ex) {
+                // FileNotFoundException or IOException
+                log.debug("getResourceAsStream: Cannot get resource {}: {}",
+                    path, ex.getMessage());
+            }
+        }
+
+        // check whether we can resolve as an URL ...
+        try {
+            // create the URL and try to access
+            URL url = getUrlForResource(path);
+            if (url != null) {
+                return url.openStream();
+            }
+        } catch (Exception e) {
+            log.debug(
+                "getResourceAsStream: Cannot access resource {} through URL: {}",
+                path, e.getMessage());
+        }
+
+        return null;
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getResourcePaths(java.lang.String)
+     */
+    public Set<?> getResourcePaths(String path) {
+        return ioProvider.getResourcePaths(path);
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#log(java.lang.String)
+     */
+    public void log(String msg) {
+        log.info(msg);
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#log(java.lang.Exception, java.lang.String)
+     */
+    @Deprecated
+    public void log(Exception exception, String msg) {
+        log(msg, exception);
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#log(java.lang.String, java.lang.Throwable)
+     */
+    public void log(String message, Throwable throwable) {
+        log.error(message, throwable);
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
+     */
+    public Object getAttribute(String name) {
+        return delegatee.getAttribute(name);
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getAttributeNames()
+     */
+    public Enumeration<?> getAttributeNames() {
+        return delegatee.getAttributeNames();
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
+     */
+    public void removeAttribute(String name) {
+        delegatee.removeAttribute(name);
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
+     */
+    public void setAttribute(String name, Object object) {
+        delegatee.setAttribute(name, object);
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getContext(java.lang.String)
+     */
+    public ServletContext getContext(String uripath) {
+        return delegatee.getContext(uripath);
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
+     */
+    public String getInitParameter(String name) {
+        return delegatee.getInitParameter(name);
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getInitParameterNames()
+     */
+    public Enumeration<?> getInitParameterNames() {
+        return delegatee.getInitParameterNames();
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getMajorVersion()
+     */
+    public int getMajorVersion() {
+        return delegatee.getMajorVersion();
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getMimeType(java.lang.String)
+     */
+    public String getMimeType(String file) {
+        return delegatee.getMimeType(file);
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getMinorVersion()
+     */
+    public int getMinorVersion() {
+        return delegatee.getMinorVersion();
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getNamedDispatcher(java.lang.String)
+     */
+    public RequestDispatcher getNamedDispatcher(String name) {
+        return delegatee.getNamedDispatcher(name);
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getRealPath(java.lang.String)
+     */
+    public String getRealPath(String path) {
+        return delegatee.getRealPath(path);
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getRequestDispatcher(java.lang.String)
+     */
+    public RequestDispatcher getRequestDispatcher(String path) {
+        return delegatee.getRequestDispatcher(path);
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getServerInfo()
+     */
+    public String getServerInfo() {
+        return delegatee.getServerInfo();
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getServlet(java.lang.String)
+     */
+    @Deprecated
+    public Servlet getServlet(String name) throws ServletException {
+        return delegatee.getServlet(name);
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getServletContextName()
+     */
+    public String getServletContextName() {
+        return delegatee.getServletContextName();
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getServletNames()
+     */
+    @Deprecated
+    public Enumeration<?> getServletNames() {
+        return delegatee.getServletNames();
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getServlets()
+     */
+    @Deprecated
+    public Enumeration<?> getServlets() {
+        return delegatee.getServlets();
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getContextPath()
+     */
+    public String getContextPath() {
+        return delegatee.getContextPath();
+    }
+
+    //---------- internal -----------------------------------------------------
+
+    private URL getUrlForResource(String path) {
+        int cs = path.indexOf(":/");
+        if (cs > 0 && cs < path.length()-2) {
+            // insert second slash after scheme (was canonicalized away)
+            cs += 2;
+            if (cs < path.length() && path.charAt(cs) != '/') {
+                path = path.substring(0, cs) + "/" + path.substring(cs);
+            }
+
+            // create the URL and try to access
+            try {
+                return new URL(path);
+            } catch (MalformedURLException mue) {
+                log.debug("getUrlForResource: Cannot create URL for {}: {}",
+                    path, mue.getMessage());
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/src/main/java/org/apache/sling/scripting/java/Options.java b/src/main/java/org/apache/sling/scripting/java/Options.java
new file mode 100644
index 0000000..bc445ac
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/java/Options.java
@@ -0,0 +1,177 @@
+/*
+ * 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.scripting.java;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Properties;
+
+import org.osgi.service.component.ComponentContext;
+
+
+/**
+ * A class to hold all init parameters specific to the compiler
+ */
+public class Options {
+
+    private static final String PROPERTY_JAVA_ENCODING = "javaEncoding";
+
+    private static final String PROPERTY_COMPILER_SOURCE_V_M = "compilerSourceVM";
+
+    private static final String PROPERTY_COMPILER_TARGET_V_M = "compilerTargetVM";
+
+    private static final String PROPERTY_DEVELOPMENT = "development";
+
+    private static final String PROPERTY_OUTPUT_PATH = "outputPath";
+
+    private static final String PROPERTY_MODIFICATION_TEST_INTERVAL = "modificationTestInterval";
+
+    private static final String PROPERTY_CLASSDEBUGINFO = "classdebuginfo";
+
+    /** Default source and target VM version (value is "1.5"). */
+    private static final String DEFAULT_VM_VERSION = "1.5";
+
+    /**
+     * Is the engine being used in development mode?
+     */
+    private final boolean development;
+
+    /**
+     * Do we want to include debugging information in the class file?
+     */
+    private final boolean classDebugInfo;
+
+    /**
+     * Compiler target VM.
+     */
+    private final String compilerTargetVM;
+
+    /**
+     * The compiler source VM.
+     */
+    private final String compilerSourceVM;
+
+    /**
+     * Java platform encoding to generate the servlet.
+     */
+    private final String javaEncoding;
+
+    /**
+     * Modification test interval.
+     */
+    private final int modificationTestInterval;
+
+    /**
+     * Classloader
+     */
+    private final ClassLoader classLoader;
+
+    private final String destinationDir;
+
+    /**
+     * Create an compiler options object using data available from
+     * the component configuration.
+     */
+    public Options(final ComponentContext componentContext,
+                   final ClassLoader classLoader) {
+
+        this.classLoader = classLoader;
+
+        // generate properties
+        final Properties properties = new Properties();
+        // set default values first
+        properties.put(PROPERTY_CLASSDEBUGINFO, "true");
+        properties.put(PROPERTY_DEVELOPMENT, "true");
+        properties.put(PROPERTY_MODIFICATION_TEST_INTERVAL, "-1");
+        properties.put(PROPERTY_COMPILER_TARGET_V_M, DEFAULT_VM_VERSION);
+        properties.put(PROPERTY_COMPILER_SOURCE_V_M, DEFAULT_VM_VERSION);
+        properties.put(PROPERTY_JAVA_ENCODING, "UTF-8");
+        properties.put(PROPERTY_OUTPUT_PATH, "/var/classes");
+
+        // now check component properties
+        Dictionary<?, ?> config = componentContext.getProperties();
+        Enumeration<?> enumeration = config.keys();
+        while (enumeration.hasMoreElements()) {
+            String key = (String) enumeration.nextElement();
+            if (key.startsWith("java.")) {
+                Object value = config.get(key);
+                if (value != null) {
+                    properties.put(key.substring("java.".length()),
+                        value.toString());
+                }
+            }
+        }
+
+        this.destinationDir = properties.get(PROPERTY_OUTPUT_PATH).toString();
+        this.classDebugInfo = Boolean.valueOf(properties.get(PROPERTY_CLASSDEBUGINFO).toString());
+        this.modificationTestInterval = Integer.valueOf(properties.get(PROPERTY_MODIFICATION_TEST_INTERVAL).toString());
+        this.development = Boolean.valueOf(properties.get(PROPERTY_DEVELOPMENT).toString());
+        this.compilerTargetVM = properties.get(PROPERTY_COMPILER_TARGET_V_M).toString();
+        this.compilerSourceVM = properties.get(PROPERTY_COMPILER_SOURCE_V_M).toString();
+        this.javaEncoding = properties.get(PROPERTY_JAVA_ENCODING).toString();
+    }
+
+    /**
+     * Return the destination directory.
+     */
+    public String getDestinationPath() {
+        return this.destinationDir;
+    }
+
+    /**
+     * Should class files be compiled with debug information?
+     */
+    public boolean getClassDebugInfo() {
+        return this.classDebugInfo;
+    }
+
+    /**
+     * Modification test interval.
+     */
+    public int getModificationTestInterval() {
+        return this.modificationTestInterval;
+    }
+
+    /**
+     * Is the engine being used in development mode?
+     */
+    public boolean getDevelopment() {
+        return this.development;
+    }
+
+    public ClassLoader getClassLoader() {
+        return this.classLoader;
+    }
+
+    /**
+     * @see Options#getCompilerTargetVM
+     */
+    public String getCompilerTargetVM() {
+        return this.compilerTargetVM;
+    }
+
+    /**
+     * @see Options#getCompilerSourceVM
+     */
+    public String getCompilerSourceVM() {
+        return this.compilerSourceVM;
+    }
+
+    public String getJavaEncoding() {
+        return this.javaEncoding;
+    }
+}
diff --git a/src/main/java/org/apache/sling/scripting/java/ServletCache.java b/src/main/java/org/apache/sling/scripting/java/ServletCache.java
new file mode 100644
index 0000000..9755621
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/java/ServletCache.java
@@ -0,0 +1,72 @@
+/*
+ * 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.scripting.java;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This is a simple in memory cache for compiled servlets.
+ */
+public final class ServletCache {
+
+    /**
+     * Maps servlet source urls to servlet wrappers.
+     */
+    private Map<String, ServletWrapper> servlets = new ConcurrentHashMap<String, ServletWrapper>();
+
+    /**
+     * Add a new ServletWrapper.
+     *
+     * @param servletUri Servlet URI
+     * @param sw Servlet wrapper
+     */
+    public void addWrapper(String servletUri, ServletWrapper sw) {
+        servlets.put(servletUri, sw);
+    }
+
+    /**
+     * Get an already existing ServletWrapper.
+     *
+     * @param servletUri Servlet URI
+     * @return ServletWrapper
+     */
+    public ServletWrapper getWrapper(String servletUri) {
+        return servlets.get(servletUri);
+    }
+
+    /**
+     * Remove a  ServletWrapper.
+     *
+     * @param servletUri Servlet URI
+     */
+    public void removeWrapper(String servletUri) {
+        servlets.remove(servletUri);
+    }
+
+    /**
+     * Process a "destory" event for this web application context.
+     */
+    public void destroy() {
+        Iterator<ServletWrapper> i = this.servlets.values().iterator();
+        while (i.hasNext()) {
+            i.next().destroy();
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/scripting/java/ServletWrapper.java b/src/main/java/org/apache/sling/scripting/java/ServletWrapper.java
new file mode 100644
index 0000000..a052c8b
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/java/ServletWrapper.java
@@ -0,0 +1,275 @@
+/*
+ * 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.scripting.java;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.SingleThreadModel;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.sling.api.SlingException;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingIOException;
+import org.apache.sling.api.SlingServletException;
+import org.apache.sling.api.scripting.SlingBindings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+
+ */
+
+public class ServletWrapper {
+
+    /** The logger. */
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    private final String servletUri;
+
+    /** The servlet config. */
+    private ServletConfig config;
+
+    /** Reload flag. */
+    private boolean reload = true;
+
+    /** Compiler options. */
+    private final Options options;
+
+    private Servlet theServlet;
+    private long available = 0L;
+    private boolean firstTime = true;
+    private ServletException compileException;
+    private CompilationContext ctxt;
+
+    /**
+     * A wrapper for servlets.
+     */
+    public ServletWrapper(final ServletConfig config,
+                          final Options options,
+                          final SlingIOProvider ioProvider,
+                          final String servletPath,
+                          final ServletCache servletCache) {
+        this.config = config;
+        this.servletUri = servletPath;
+        this.options = options;
+        this.ctxt = new CompilationContext(servletUri, options,
+                ioProvider, servletCache, this);
+    }
+
+    /**
+     * Set the reload flag.
+     * @param reload
+     */
+    public void setReload(boolean reload) {
+        this.reload = reload;
+    }
+
+    /**
+     * Get the servlet.
+     * @return The servlet.
+     * @throws ServletException
+     * @throws IOException
+     * @throws FileNotFoundException
+     */
+    private Servlet getServlet()
+    throws ServletException, IOException, FileNotFoundException {
+        if (reload) {
+            synchronized (this) {
+                if (reload) {
+                    destroy();
+
+                    Servlet servlet = null;
+
+                    try {
+                        final Class servletClass = ctxt.load();
+                        servlet = (Servlet) servletClass.newInstance();
+                    } catch (IllegalAccessException e) {
+                        throw new ServletException(e);
+                    } catch (InstantiationException e) {
+                        throw new ServletException(e);
+                    } catch (Exception e) {
+                        throw new ServletException(e);
+                    }
+
+                    servlet.init(config);
+
+                    theServlet = servlet;
+                    reload = false;
+                }
+            }
+        }
+        return theServlet;
+    }
+
+    /**
+     * Sets the compilation exception for this ServletWrapper.
+     *
+     * @param je The compilation exception
+     */
+    public void setCompilationException(ServletException je) {
+        this.compileException = je;
+    }
+
+    /**
+     * @param bindings
+     * @throws SlingIOException
+     * @throws SlingServletException
+     * @throws IllegalArgumentException if the Jasper Precompile controller
+     *             request parameter has an illegal value.
+     */
+    public void service(SlingBindings bindings) {
+        final SlingHttpServletRequest request = bindings.getRequest();
+        final Object oldValue = request.getAttribute(SlingBindings.class.getName());
+        try {
+            request.setAttribute(SlingBindings.class.getName(), bindings);
+            service(request, bindings.getResponse());
+        } catch (SlingException se) {
+            // rethrow as is
+            throw se;
+        } catch (IOException ioe) {
+            throw new SlingIOException(ioe);
+        } catch (ServletException se) {
+            throw new SlingServletException(se);
+        } finally {
+            request.setAttribute(SlingBindings.class.getName(), oldValue);
+        }
+    }
+
+    /**
+     * Call the servlet.
+     * @param request The current request.
+     * @param response The current response.
+     * @throws ServletException
+     * @throws IOException
+     * @throws FileNotFoundException
+     */
+    public void service(HttpServletRequest request,
+                        HttpServletResponse response)
+    throws ServletException, IOException, FileNotFoundException {
+
+        try {
+
+            if (ctxt.isRemoved()) {
+                throw new FileNotFoundException(servletUri);
+            }
+
+            if ((available > 0L) && (available < Long.MAX_VALUE)) {
+                if (available > System.currentTimeMillis()) {
+                    response.setDateHeader("Retry-After", available);
+                    response.sendError
+                        (HttpServletResponse.SC_SERVICE_UNAVAILABLE,
+                         "Servlet unavailable.");
+                    return;
+                }
+                // Wait period has expired. Reset.
+                available = 0;
+            }
+
+            /*
+             * (1) Compile
+             */
+            if (options.getDevelopment() || firstTime ) {
+                synchronized (this) {
+                    firstTime = false;
+
+                    // The following sets reload to true, if necessary
+                    ctxt.compile();
+                }
+            } else {
+                if (compileException != null) {
+                    // Throw cached compilation exception
+                    throw compileException;
+                }
+            }
+
+            /*
+             * (2) (Re)load servlet class file
+             */
+            getServlet();
+
+        } catch (FileNotFoundException ex) {
+            ctxt.incrementRemoved();
+            try {
+                response.sendError(HttpServletResponse.SC_NOT_FOUND,
+                                  ex.getMessage());
+            } catch (IllegalStateException ise) {
+                logger.error("Java servlet source not found." +
+                       ex.getMessage(), ex);
+            }
+        } catch (ServletException ex) {
+            throw ex;
+        } catch (IOException ex) {
+            throw ex;
+        } catch (IllegalStateException ex) {
+            throw ex;
+        } catch (Exception ex) {
+            throw new ServletException(ex);
+        }
+
+        try {
+
+            /*
+             * (3) Service request
+             */
+            if (theServlet instanceof SingleThreadModel) {
+               // sync on the wrapper so that the freshness
+               // of the page is determined right before servicing
+               synchronized (this) {
+                   theServlet.service(request, response);
+                }
+            } else {
+                theServlet.service(request, response);
+            }
+
+        } catch (UnavailableException ex) {
+            int unavailableSeconds = ex.getUnavailableSeconds();
+            if (unavailableSeconds <= 0) {
+                unavailableSeconds = 60;        // Arbitrary default
+            }
+            available = System.currentTimeMillis() +
+                (unavailableSeconds * 1000L);
+            response.sendError
+                (HttpServletResponse.SC_SERVICE_UNAVAILABLE,
+                 ex.getMessage());
+        } catch (ServletException ex) {
+            throw ex;
+        } catch (IOException ex) {
+            throw ex;
+        } catch (IllegalStateException ex) {
+            throw ex;
+        } catch (Exception ex) {
+            throw new ServletException(ex);
+        }
+    }
+
+    /**
+     * Destroy the servlet.
+     */
+    public void destroy() {
+        if (theServlet != null) {
+            theServlet.destroy();
+            theServlet = null;
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/scripting/java/SlingIOProvider.java b/src/main/java/org/apache/sling/scripting/java/SlingIOProvider.java
new file mode 100644
index 0000000..00e714a
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/java/SlingIOProvider.java
@@ -0,0 +1,369 @@
+/*
+ * 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.scripting.java;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.sling.api.SlingException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceMetadata;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The <code>SlingIOProvider</code> TODO
+ */
+public class SlingIOProvider  {
+
+    /** default log */
+    private static final Logger log = LoggerFactory.getLogger(SlingIOProvider.class);
+
+    private final SlingRepository repository;
+
+    private ThreadLocal<ResourceResolver> requestResourceResolver;
+
+    // private session for write access
+    private ThreadLocal<Session> privateSession;
+
+    SlingIOProvider(SlingRepository repository) {
+        this.repository = repository;
+        this.requestResourceResolver = new ThreadLocal<ResourceResolver>();
+        this.privateSession = new ThreadLocal<Session>();
+    }
+
+    void setRequestResourceResolver(ResourceResolver resolver) {
+        requestResourceResolver.set(resolver);
+    }
+
+    void resetRequestResourceResolver() {
+        requestResourceResolver.remove();
+
+        // at the same time logout this thread's session
+        Session session = privateSession.get();
+        if (session != null) {
+            if (session.isLive()) {
+                session.logout();
+            }
+            privateSession.remove();
+        }
+    }
+
+    // ---------- IOProvider interface -----------------------------------------
+
+    /**
+     * Returns an InputStream for the file name which is looked up with the
+     * ResourceProvider and retrieved from the Resource if the StreamProvider
+     * interface is implemented.
+     */
+    public InputStream getInputStream(String fileName)
+    throws FileNotFoundException, IOException {
+
+        try {
+
+            Resource resource = getResourceInternal(fileName);
+            if (resource == null) {
+                throw new FileNotFoundException("Cannot find " + fileName);
+            }
+
+            InputStream stream = resource.adaptTo(InputStream.class);
+            if (stream == null) {
+                throw new FileNotFoundException("Cannot find " + fileName);
+            }
+
+            return stream;
+
+        } catch (SlingException se) {
+            throw (IOException) new IOException(
+                "Failed to get InputStream for " + fileName).initCause(se);
+        }
+    }
+
+    /**
+     * Returns the value of the last modified meta data field of the resource
+     * found at file name or zero if the meta data field is not set. If the
+     * resource does not exist or an error occurrs finding the resource, -1 is
+     * returned.
+     */
+    public long lastModified(String fileName) {
+        try {
+            Resource resource = getResourceInternal(fileName);
+            if (resource != null) {
+                ResourceMetadata meta = resource.getResourceMetadata();
+                long modTime = meta.getModificationTime();
+                return (modTime > 0) ? modTime : 0;
+            }
+
+        } catch (SlingException se) {
+            log.error("Cannot get last modification time for " + fileName, se);
+        }
+
+        // fallback to "non-existant" in case of problems
+        return -1;
+    }
+
+    /**
+     * Removes the named item from the repository.
+     */
+    public boolean delete(String fileName) {
+        Node parentNode = null;
+        try {
+            fileName = cleanPath(fileName);
+            Session session = getPrivateSession();
+            if (session.itemExists(fileName)) {
+                Item fileItem = session.getItem(fileName);
+                parentNode = fileItem.getParent();
+                fileItem.remove();
+                parentNode.save();
+                return true;
+            }
+        } catch (RepositoryException re) {
+            log.error("Cannot remove " + fileName, re);
+        } finally {
+            checkNode(parentNode, fileName);
+        }
+
+        // fall back to false if item does not exist or in case of error
+        return false;
+    }
+
+    /**
+     * Returns an output stream to write to the repository.
+     */
+    public OutputStream getOutputStream(String fileName) {
+        fileName = cleanPath(fileName);
+        return new RepositoryOutputStream(this, fileName);
+    }
+
+    /* package */URL getURL(String path) throws MalformedURLException {
+        try {
+            Resource resource = getResourceInternal(path);
+            return (resource != null) ? resource.adaptTo(URL.class) : null;
+        } catch (SlingException se) {
+            throw (MalformedURLException) new MalformedURLException(
+                "Cannot get URL for " + path).initCause(se);
+        }
+    }
+
+    /* package */Set<String> getResourcePaths(String path) {
+        Set<String> paths = new HashSet<String>();
+
+        ResourceResolver resolver = requestResourceResolver.get();
+        if (resolver != null) {
+            try {
+                Resource resource = resolver.getResource(cleanPath(path));
+                if (resource != null) {
+                    Iterator<Resource> entries = resolver.listChildren(resource);
+                    while (entries.hasNext()) {
+                        paths.add(entries.next().getPath());
+                    }
+                }
+            } catch (SlingException se) {
+                log.warn("getResourcePaths: Cannot list children of " + path,
+                    se);
+            }
+        }
+
+        return paths.isEmpty() ? null : paths;
+    }
+
+    private Resource getResourceInternal(String path) throws SlingException {
+        ResourceResolver resolver = requestResourceResolver.get();
+        if (resolver != null) {
+            return resolver.getResource(cleanPath(path));
+        }
+
+        return null;
+    }
+
+    // ---------- internal -----------------------------------------------------
+
+    private Session getPrivateSession() throws RepositoryException {
+        Session session = privateSession.get();
+        if (session == null) {
+            session = repository.loginAdministrative(null);
+            privateSession.set(session);
+        }
+
+        return session;
+    }
+
+    private static void checkNode(Node node, String path) {
+        if (node != null && node.isModified()) {
+            try {
+                node.refresh(false);
+            } catch (RepositoryException re) {
+                log.error("Cannot refresh node for " + path
+                    + " after failed save", re);
+            }
+        }
+    }
+
+    private String cleanPath(String path) {
+        // replace backslash by slash
+        path = path.replace('\\', '/');
+
+        // cut off trailing slash
+        while (path.endsWith("/")) {
+            path = path.substring(0, path.length() - 1);
+        }
+
+        return path;
+    }
+
+    private static class RepositoryOutputStream extends ByteArrayOutputStream {
+
+        private final SlingIOProvider repositoryOutputProvider;
+
+        private final String fileName;
+
+        RepositoryOutputStream(SlingIOProvider repositoryOutputProvider,
+                String fileName) {
+            this.repositoryOutputProvider = repositoryOutputProvider;
+            this.fileName = fileName;
+        }
+
+        public void close() throws IOException {
+            super.close();
+
+            Node parentNode = null;
+            try {
+                Session session = repositoryOutputProvider.getPrivateSession();
+                Node fileNode = null;
+                Node contentNode = null;
+                if (session.itemExists(fileName)) {
+                    Item item = session.getItem(fileName);
+                    if (item.isNode()) {
+                        Node node = item.isNode()
+                                ? (Node) item
+                                : item.getParent();
+                        if ("jcr:content".equals(node.getName())) {
+                            // replace the content properties of the jcr:content
+                            // node
+                            parentNode = node;
+                            contentNode = node;
+                        } else if (node.isNodeType("nt:file")) {
+                            // try to set the content properties of jcr:content
+                            // node
+                            parentNode = node;
+                            contentNode = node.getNode("jcr:content");
+                        } else { // fileName is a node
+                            // try to set the content properties of the node
+                            parentNode = node;
+                            contentNode = node;
+                        }
+                    } else {
+                        // replace property with an nt:file node (if possible)
+                        parentNode = item.getParent();
+                        String name = item.getName();
+                        fileNode = parentNode.addNode(name, "nt:file");
+                        item.remove();
+                    }
+                } else {
+                    fileNode = createPath(fileName, "nt:folder", "nt:file", session);
+                    parentNode = session.getRootNode();
+                }
+
+                // if we have a file node, create the contentNode
+                if (fileNode != null) {
+                    contentNode = fileNode.addNode("jcr:content", "nt:resource");
+                }
+
+                contentNode.setProperty("jcr:lastModified",
+                    System.currentTimeMillis());
+                contentNode.setProperty("jcr:data", new ByteArrayInputStream(
+                    buf, 0, size()));
+                contentNode.setProperty("jcr:mimeType",
+                    "application/octet-stream");
+
+                parentNode.save();
+            } catch (RepositoryException re) {
+                log.error("Cannot write file " + fileName, re);
+                throw new IOException("Cannot write file " + fileName
+                    + ", reason: " + re.toString());
+            } finally {
+                checkNode(parentNode, fileName);
+            }
+        }
+    }
+
+    /**
+     * Creates or gets the {@link javax.jcr.Node Node} at the given Path.
+     * In case it has to create the Node all non-existent intermediate path-elements
+     * will be create with the given intermediate node type and the returned node
+     * will be created with the given nodeType
+     *
+     * @param path to create
+     * @param intermediateNodeType to use for creation of intermediate nodes
+     * @param nodeType to use for creation of the final node
+     * @param session to use
+     * @return the Node at path
+     * @throws RepositoryException in case of exception accessing the Repository
+     */
+    private static Node createPath(String path,
+                                  String intermediateNodeType,
+                                  String nodeType,
+                                  Session session)
+            throws RepositoryException {
+        if (path == null || path.length() == 0 || "/".equals(path)) {
+            return session.getRootNode();
+        } else if (!session.itemExists(path)) {
+            Node node = session.getRootNode();
+            path = path.substring(1);
+            int pos = path.lastIndexOf('/');
+            if ( pos != -1 ) {
+                final StringTokenizer st = new StringTokenizer(path.substring(0, pos), "/");
+                while ( st.hasMoreTokens() ) {
+                    final String token = st.nextToken();
+                    if ( !node.hasNode(token) ) {
+                        try {
+                            node.addNode(token, intermediateNodeType);
+                        } catch (RepositoryException re) {
+                            // we ignore this as this folder might be created from a different task
+                            node.refresh(false);
+                        }
+                    }
+                    node = node.getNode(token);
+                }
+                path = path.substring(pos + 1);
+            }
+            if ( !node.hasNode(path) ) {
+                node.addNode(path, nodeType);
+            }
+            return node.getNode(path);
+        } else {
+            return (Node) session.getItem(path);
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/scripting/java/jdt/CompilationUnit.java b/src/main/java/org/apache/sling/scripting/java/jdt/CompilationUnit.java
new file mode 100644
index 0000000..80459f4
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/java/jdt/CompilationUnit.java
@@ -0,0 +1,282 @@
+/*
+ * 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.scripting.java.jdt;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+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 java.util.LinkedList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.apache.sling.scripting.java.CompilerError;
+import org.apache.sling.scripting.java.Options;
+import org.apache.sling.scripting.java.SlingIOProvider;
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.internal.compiler.ClassFile;
+import org.eclipse.jdt.internal.compiler.CompilationResult;
+import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
+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;
+
+
+public class CompilationUnit
+    implements ICompilationUnit, INameEnvironment, ICompilerRequestor {
+
+    private final Options options;
+    private final SlingIOProvider ioProvider;
+    private final String className;
+    private final String sourceFile;
+
+    /** The list of compile errors. */
+    private final List<CompilerError> errors = new LinkedList<CompilerError>();
+
+    public CompilationUnit(String sourceFile,
+                           String className,
+                           Options options,
+                           SlingIOProvider ioProvider) {
+        this.className = className;
+        this.sourceFile = sourceFile;
+        this.ioProvider = ioProvider;
+        this.options = options;
+    }
+
+    /**
+     * @see org.eclipse.jdt.internal.compiler.env.IDependent#getFileName()
+     */
+    public char[] getFileName() {
+        return className.concat(".java").toCharArray();
+    }
+
+    /**
+     * @see org.eclipse.jdt.internal.compiler.env.ICompilationUnit#getContents()
+     */
+    public char[] getContents() {
+        char[] result = null;
+        InputStream fr = null;
+        try {
+            fr = ioProvider.getInputStream(this.sourceFile);
+            final Reader reader = new BufferedReader(new InputStreamReader(fr, this.options.getJavaEncoding()));
+            try {
+                char[] chars = new char[8192];
+                StringBuffer buf = new StringBuffer();
+                int count;
+                while ((count = reader.read(chars, 0, chars.length)) > 0) {
+                    buf.append(chars, 0, count);
+                }
+                result = new char[buf.length()];
+                buf.getChars(0, result.length, result, 0);
+            } finally {
+                reader.close();
+            }
+        } catch (IOException e) {
+            handleError(-1, -1, e.getMessage());
+        }
+        return result;
+    }
+
+    /**
+     * @see org.eclipse.jdt.internal.compiler.env.ICompilationUnit#getMainTypeName()
+     */
+    public char[] getMainTypeName() {
+        int dot = className.lastIndexOf('.');
+        if (dot > 0) {
+            return className.substring(dot + 1).toCharArray();
+        }
+        return className.toCharArray();
+    }
+
+    /**
+     * @see org.eclipse.jdt.internal.compiler.env.ICompilationUnit#getPackageName()
+     */
+    public char[][] getPackageName() {
+        StringTokenizer izer = new StringTokenizer(className.replace('/', '.'), ".");
+        char[][] result = new char[izer.countTokens()-1][];
+        for (int i = 0; i < result.length; i++) {
+            String tok = izer.nextToken();
+            result[i] = tok.toCharArray();
+        }
+        return result;
+    }
+
+    /**
+     * @see org.eclipse.jdt.internal.compiler.env.INameEnvironment#findType(char[][])
+     */
+    public NameEnvironmentAnswer findType(char[][] compoundTypeName) {
+        StringBuffer result = new StringBuffer();
+        for (int i = 0; i < compoundTypeName.length; i++) {
+            if (i > 0) {
+                result.append(".");
+            }
+            result.append(compoundTypeName[i]);
+        }
+        return findType(result.toString());
+    }
+
+    /**
+     * @see org.eclipse.jdt.internal.compiler.env.INameEnvironment#findType(char[], char[][])
+     */
+    public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) {
+        StringBuffer result = new StringBuffer();
+        for (int i = 0; i < packageName.length; i++) {
+            if (i > 0) {
+                result.append(".");
+            }
+            result.append(packageName[i]);
+        }
+        result.append(".");
+        result.append(typeName);
+        return findType(result.toString());
+    }
+
+    /**
+     * @param className
+     * @return
+     */
+    private NameEnvironmentAnswer findType(String className) {
+        try {
+            if (className.equals(this.className)) {
+                ICompilationUnit compilationUnit = this;
+                return new NameEnvironmentAnswer(compilationUnit, null);
+            }
+            String resourceName = className.replace('.', '/') + ".class";
+            InputStream is = options.getClassLoader().getResourceAsStream(resourceName);
+            if (is != null) {
+                byte[] classBytes;
+                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();
+                classBytes = baos.toByteArray();
+                char[] fileName = className.toCharArray();
+                ClassFileReader classFileReader =
+                    new ClassFileReader(classBytes, fileName,
+                                        true);
+                return
+                    new NameEnvironmentAnswer(classFileReader, null);
+            }
+        } catch (IOException exc) {
+            handleError(-1, -1, exc.getMessage());
+        } catch (org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException exc) {
+            handleError(-1, -1, exc.getMessage());
+        }
+        return null;
+    }
+
+    private boolean isPackage(String result) {
+        if (result.equals(this.className)) {
+            return false;
+        }
+        String resourceName = result.replace('.', '/') + ".class";
+        InputStream is = options.getClassLoader().getResourceAsStream(resourceName);
+        return is == null;
+    }
+
+    /**
+     * @see org.eclipse.jdt.internal.compiler.env.INameEnvironment#isPackage(char[][], char[])
+     */
+    public boolean isPackage(char[][] parentPackageName, char[] packageName) {
+        StringBuffer result = new StringBuffer();
+        if (parentPackageName != null) {
+            for (int i = 0; i < parentPackageName.length; i++) {
+                if (i > 0) {
+                    result.append(".");
+                }
+                result.append(parentPackageName[i]);
+            }
+        }
+        String str = new String(packageName);
+        if (Character.isUpperCase(str.charAt(0)) && !isPackage(result.toString())) {
+                return false;
+        }
+        result.append(".");
+        result.append(str);
+        return isPackage(result.toString());
+    }
+
+    /**
+     * @see org.eclipse.jdt.internal.compiler.env.INameEnvironment#cleanup()
+     */
+    public void cleanup() {
+        // EMPTY
+    }
+
+    /**
+     * @see org.eclipse.jdt.internal.compiler.ICompilerRequestor#acceptResult(org.eclipse.jdt.internal.compiler.CompilationResult)
+     */
+    public void acceptResult(CompilationResult result) {
+        try {
+            if (result.hasErrors()) {
+                IProblem[] errors = result.getErrors();
+                for (int i = 0; i < errors.length; i++) {
+                    IProblem error = errors[i];
+                    handleError(error.getSourceLineNumber(), -1, error.getMessage());
+                }
+            } else {
+                ClassFile[] classFiles = result.getClassFiles();
+                for (int i = 0; i < classFiles.length; i++) {
+                    ClassFile classFile = classFiles[i];
+                    char[][] compoundName = classFile.getCompoundName();
+                    StringBuffer className = new StringBuffer();
+                    for (int j = 0;  j < compoundName.length; j++) {
+                        if (j > 0) {
+                            className.append(".");
+                        }
+                        className.append(compoundName[j]);
+                    }
+                    byte[] bytes = classFile.getBytes();
+                    final StringBuffer b = new StringBuffer(this.options.getDestinationPath());
+                    b.append('/');
+                    b.append(className.toString().replace('.', '/'));
+                    b.append(".class");
+                    OutputStream fout = ioProvider.getOutputStream(b.toString());
+                    BufferedOutputStream bos = new BufferedOutputStream(fout);
+                    bos.write(bytes);
+                    bos.close();
+                }
+            }
+        } catch (IOException exc) {
+            exc.printStackTrace();
+        }
+    }
+
+    private void handleError(int line, int column, Object errorMessage) {
+        if (column < 0) column = 0;
+        errors.add(new CompilerError(this.sourceFile,
+                                     true,
+                                     line,
+                                     column,
+                                     line,
+                                     column,
+                                     errorMessage.toString()));
+    }
+
+    public List<CompilerError> getErrors() throws IOException {
+        return errors;
+    }
+}
diff --git a/src/main/java/org/apache/sling/scripting/java/jdt/EclipseJavaCompiler.java b/src/main/java/org/apache/sling/scripting/java/jdt/EclipseJavaCompiler.java
new file mode 100644
index 0000000..8ed73af
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/java/jdt/EclipseJavaCompiler.java
@@ -0,0 +1,118 @@
+/*
+ * 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.scripting.java.jdt;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.sling.scripting.java.CompilationContext;
+import org.apache.sling.scripting.java.CompilerError;
+import org.apache.sling.scripting.java.Options;
+import org.apache.sling.scripting.java.SlingIOProvider;
+import org.eclipse.jdt.internal.compiler.Compiler;
+import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
+import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
+import org.eclipse.jdt.internal.compiler.IProblemFactory;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
+
+/**
+ * Eclipse Java Compiler
+ *
+ * @version $Id$
+ */
+public class EclipseJavaCompiler {
+
+    /** The io provider. */
+    private final SlingIOProvider ioProvider;
+
+    /** The compiler options. */
+    private final Options compilerOptions;
+
+    /** The compilation context. */
+    private final CompilationContext context;
+
+    /**
+     * Construct a new java compiler.
+     * @param context
+     */
+    public EclipseJavaCompiler(final CompilationContext context) {
+        this.ioProvider = context.getIOProvider();
+        this.compilerOptions = context.getCompilerOptions();
+        this.context = context;
+    }
+
+    private CompilerOptions getCompilerOptions() {
+        CompilerOptions options = new CompilerOptions();
+        final Map<String, String> settings = new HashMap<String, String>();
+        settings.put(CompilerOptions.OPTION_LineNumberAttribute,
+                CompilerOptions.GENERATE);
+        settings.put(CompilerOptions.OPTION_SourceFileAttribute,
+                CompilerOptions.GENERATE);
+        settings.put(CompilerOptions.OPTION_ReportDeprecation,
+                CompilerOptions.IGNORE);
+        settings.put(CompilerOptions.OPTION_ReportUnusedImport, CompilerOptions.IGNORE);
+        settings.put(CompilerOptions.OPTION_Encoding, this.compilerOptions.getJavaEncoding());
+        if (this.compilerOptions.getClassDebugInfo()) {
+            settings.put(CompilerOptions.OPTION_LocalVariableAttribute, CompilerOptions.GENERATE);
+        }
+        if ( this.compilerOptions.getCompilerSourceVM().equals("1.6") ) {
+            settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_1_6);
+        } else {
+            settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_1_5);
+        }
+        if ( this.compilerOptions.getCompilerTargetVM().equals("1.6") ) {
+            settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_1_6);
+            settings.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_1_6);
+        } else {
+            settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_1_5);
+            settings.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_1_5);
+        }
+
+        options.set(settings);
+        return options;
+    }
+
+    /**
+     * Compile the java class.
+     * @return null if no error occured, a list of errors otherwise.
+     * @throws IOException
+     */
+    public List<CompilerError> compile() throws IOException {
+        final IErrorHandlingPolicy policy = DefaultErrorHandlingPolicies.proceedWithAllProblems();
+        final IProblemFactory problemFactory = new DefaultProblemFactory(Locale.getDefault());
+        final CompilationUnit unit = new CompilationUnit(this.context.getSourcePath(),
+                 this.context.getJavaClassName(),
+                 this.context.getCompilerOptions(),
+                 this.ioProvider);
+
+        final Compiler compiler = new Compiler(unit,
+                                         policy,
+                                         getCompilerOptions(),
+                                         unit,
+                                         problemFactory);
+        compiler.compile(new CompilationUnit[] {unit});
+        if ( unit.getErrors().size() == 0 ) {
+            return null;
+        }
+        return unit.getErrors();
+    }
+
+}

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.