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