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>.