Skip to content
This repository has been archived by the owner on May 26, 2020. It is now read-only.

Commit

Permalink
Add JavaCompilerUtility and deprecate JavaCompilerUtil
Browse files Browse the repository at this point in the history
  • Loading branch information
mapingo committed May 1, 2018
1 parent dca3445 commit df92bb3
Show file tree
Hide file tree
Showing 4 changed files with 363 additions and 156 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ on [Keep a CHANGELOG](http://keepachangelog.com/). This project adheres to

## [Unreleased]

### Added
- JavaCompilerUtility to fix out of memory error

### Changed
- Deprecate JavaCompilerUtil and convert to use JavaCompilerUtility

## [1.16.2] - 2018-04-11

### Fixed
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package uk.gov.justice.services.test.utils.core.compiler;

import static java.text.MessageFormat.format;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toList;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.stream.Stream;

import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Compiler {

private static final Logger LOGGER = LoggerFactory.getLogger(Compiler.class);

private final JavaCompiler javaCompiler;

public Compiler() {
this.javaCompiler = ToolProvider.getSystemJavaCompiler();
}

public void compile(final File codegenOutputDir, final File compilationOutputDir) {

final DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();

try (final StandardJavaFileManager fileManager = javaCompiler.getStandardFileManager(diagnostics, Locale.getDefault(), null)) {

final List<JavaFileObject> javaObjects = scanForJavaObjects(codegenOutputDir, fileManager);

if (javaObjects.isEmpty()) {
throw new CompilationException(
format("There are no source files to compile in {0}", codegenOutputDir.getAbsolutePath()));
}

final String[] compileOptions = new String[]{"-d", compilationOutputDir.getAbsolutePath()};
final Iterable<String> compilationOptions = Arrays.asList(compileOptions);

final JavaCompiler.CompilationTask compilerTask = javaCompiler.getTask(null, fileManager, diagnostics, compilationOptions, null,
javaObjects);

if (!compilerTask.call()) {
for (Diagnostic<?> diagnostic : diagnostics.getDiagnostics()) {
LOGGER.error("Error on line {} in {}", diagnostic.getLineNumber(), diagnostic);
}
throw new CompilationException("Could not compile project");
}

} catch (final IOException e) {
throw new CompilationException("Could not compile project", e);
}
}

private List<JavaFileObject> scanForJavaObjects(final File dir, final StandardJavaFileManager fileManager) {
return scanRecursivelyForJavaObjects(dir, fileManager).collect(toList());
}

private Stream<JavaFileObject> scanRecursivelyForJavaObjects(final File dir, final StandardJavaFileManager fileManager) {

final File[] fileList = dir.listFiles();

if (null == fileList) {
return Stream.empty();
}

return stream(fileList)
.flatMap(file -> processFileForJavaObjects(fileManager, file));

}

private Stream<? extends JavaFileObject> processFileForJavaObjects(final StandardJavaFileManager fileManager, final File file) {
if (file.isDirectory()) {
return scanRecursivelyForJavaObjects(file, fileManager);
} else if (file.isFile() && file.getName().toLowerCase().endsWith(".java")) {

try {
FileUtils.readFileToString(file);
} catch (final IOException e) {
LOGGER.warn("Could not read file : {0}", file.getAbsolutePath(), e);
}

return readJavaObjects(file, fileManager);
}

return Stream.empty();
}

private Stream<JavaFileObject> readJavaObjects(final File file, final StandardJavaFileManager fileManager) {
final Iterator<? extends JavaFileObject> javaFileObjects = fileManager.getJavaFileObjects(file).iterator();

if (!javaFileObjects.hasNext()) {
throw new CompilationException(format("Could not load {0} java file object", file.getAbsolutePath()));
}

final Stream.Builder<JavaFileObject> builder = Stream.builder();

javaFileObjects.forEachRemaining(o -> builder.accept(o));

return builder.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,49 +1,23 @@
package uk.gov.justice.services.test.utils.core.compiler;

import static com.google.common.collect.Sets.newHashSet;
import static java.lang.String.join;
import static java.text.MessageFormat.format;
import static java.util.stream.Collectors.toSet;
import static org.reflections.ReflectionUtils.forNames;
import static uk.gov.justice.services.test.utils.core.compiler.JavaCompilerUtility.javaCompilerUtil;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;

import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

import com.google.common.collect.Multimap;
import org.apache.commons.io.FileUtils;
import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Compiles and loads classes and interfaces from the specified folders
*
* @deprecated Use {@link JavaCompilerUtility}
*/
@Deprecated
public class JavaCompilerUtil {

private static final Logger LOG = LoggerFactory.getLogger(JavaCompilerUtil.class);

private final File codegenOutputDir;
private final File compilationOutputDir;

private final JavaCompilerUtility javaCompilerUtility = javaCompilerUtil();

public JavaCompilerUtil(final File codegenOutputDir, final File compilationOutputDir) {
this.codegenOutputDir = codegenOutputDir;
this.compilationOutputDir = compilationOutputDir;
Expand All @@ -57,11 +31,7 @@ public JavaCompilerUtil(final File codegenOutputDir, final File compilationOutpu
* @throws IllegalStateException - if more or less than one classes found
*/
public Class<?> compiledClassOf(final String basePackage) {
final Set<Class<?>> resourceClasses = compiledClassesOf(basePackage);
if (resourceClasses.size() != 1) {
throw new IllegalStateException(format("Expected to find single class but found {0}", resourceClasses));
}
return resourceClasses.iterator().next();
return javaCompilerUtility.compiledClassOf(codegenOutputDir, compilationOutputDir, basePackage);
}

/**
Expand All @@ -73,7 +43,7 @@ public Class<?> compiledClassOf(final String basePackage) {
* @throws IllegalStateException - if more or less than one classes found
*/
public Class<?> compiledClassOf(final String basePackage, final String... additionalFilterElements) {
return compiledClassOf(basePackage, c -> !c.isInterface(), additionalFilterElements);
return javaCompilerUtility.compiledClassOf(codegenOutputDir, compilationOutputDir, basePackage, additionalFilterElements);
}

/**
Expand All @@ -85,16 +55,11 @@ public Class<?> compiledClassOf(final String basePackage, final String... additi
* @throws IllegalStateException - if more or less than one classes found
*/
public Class<?> compiledInterfaceClassOf(final String basePackage, final String... additionalFilterElements) {
return compiledClassOf(basePackage, Class::isInterface, additionalFilterElements);
return javaCompilerUtility.compiledInterfaceClassOf(codegenOutputDir, compilationOutputDir, basePackage, additionalFilterElements);
}

public Class<?> classOf(final Set<Class<?>> classes, final String basePackage, final String... additionalFilterElements) {
final Set<Class<?>> resourceClasses = classesMatching(classes,
c -> !c.isInterface() && c.getName().equals(join(".", basePackage, join(".", additionalFilterElements))));
if (resourceClasses.size() != 1) {
throw new IllegalStateException(format("Expected to find single class but found {0}", resourceClasses));
}
return resourceClasses.iterator().next();
return javaCompilerUtility.classOf(classes, basePackage, additionalFilterElements);
}

/**
Expand All @@ -105,13 +70,7 @@ public Class<?> classOf(final Set<Class<?>> classes, final String basePackage, f
* @throws IllegalStateException - if more or less than one interfaces found
*/
public Class<?> compiledInterfaceOf(final String basePackageName) {
final Set<Class<?>> resourceInterfaces = compiledInterfacesOf(basePackageName);
if (resourceInterfaces.size() != 1) {
throw new IllegalStateException(
format("Expected to find single interface but found {0}", resourceInterfaces));

}
return resourceInterfaces.iterator().next();
return javaCompilerUtility.compiledInterfaceOf(codegenOutputDir, compilationOutputDir, basePackageName);
}

/**
Expand All @@ -121,7 +80,7 @@ public Class<?> compiledInterfaceOf(final String basePackageName) {
* @return the set of classes
*/
public Set<Class<?>> compiledClassesOf(final String basePackage) {
return compiledClassesAndInterfaces(c -> !c.isInterface(), basePackage);
return javaCompilerUtility.compiledClassesOf(codegenOutputDir, compilationOutputDir, basePackage);
}

/**
Expand All @@ -131,108 +90,6 @@ public Set<Class<?>> compiledClassesOf(final String basePackage) {
* @return the set of classes
*/
public Set<Class<?>> compiledInterfacesOf(final String basePackage) {
return compiledClassesAndInterfaces(Class::isInterface, basePackage);
}

private Class<?> compiledClassOf(final String basePackage,
final Predicate<Class<?>> predicate,
final String... additionalFilterElements) {

final Set<Class<?>> resourceClasses = compiledClassesAndInterfaces(
c -> predicate.test(c) && c.getName().equals(join(".", basePackage, join(".", additionalFilterElements))), basePackage);
if (resourceClasses.size() != 1) {
throw new IllegalStateException(format("Expected to find single class but found {0}", resourceClasses));
}
return resourceClasses.iterator().next();
}

private Set<Class<?>> compiledClassesAndInterfaces(final Predicate<? super Class<?>> predicate,
final String basePackage) {
return classesMatching(compile(basePackage), predicate);
}

private Set<Class<?>> classesMatching(final Set<Class<?>> classes,
final Predicate<? super Class<?>> predicate) {
return classes.stream().filter(predicate).collect(toSet());
}

private Set<Class<?>> compile(final String basePackage) {
compile();
return loadClasses(basePackage);
}

private Set<Class<?>> loadClasses(final String basePackage) {
try (URLClassLoader resourceClassLoader = new URLClassLoader(new URL[]{compilationOutputDir.toURI().toURL()})) {
final Reflections reflections = new Reflections(basePackage, new SubTypesScanner(false), resourceClassLoader);
return newHashSet(forNames(getClassNames(reflections), resourceClassLoader));
} catch (IOException ex) {
throw new CompilationException("Error creating class loader", ex);
}
return javaCompilerUtility.compiledInterfacesOf(codegenOutputDir, compilationOutputDir, basePackage);
}

private Set<String> getClassNames(final Reflections reflections) {
Multimap<String, String> types = reflections.getStore().get(SubTypesScanner.class.getSimpleName());
return Stream.concat(types.values().stream(), types.keySet().stream()).collect(toSet());
}

private void compile() {

final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
final DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();

try (final StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, Locale.getDefault(), null)) {

final List<JavaFileObject> javaObjects = scanRecursivelyForJavaObjects(codegenOutputDir, fileManager);

if (javaObjects.isEmpty()) {
throw new CompilationException(
format("There are no source files to compile in {0}", codegenOutputDir.getAbsolutePath()));
}
final String[] compileOptions = new String[]{"-d", compilationOutputDir.getAbsolutePath()};
final Iterable<String> compilationOptions = Arrays.asList(compileOptions);

final CompilationTask compilerTask = compiler.getTask(null, fileManager, diagnostics, compilationOptions, null,
javaObjects);

if (!compilerTask.call()) {
for (Diagnostic<?> diagnostic : diagnostics.getDiagnostics()) {
LOG.error("Error on line {} in {}", diagnostic.getLineNumber(), diagnostic);
}
throw new CompilationException("Could not compile project");
}

} catch (final IOException e) {
throw new CompilationException("Could not compile project", e);
}
}

private List<JavaFileObject> scanRecursivelyForJavaObjects(final File dir, final StandardJavaFileManager fileManager) {
final List<JavaFileObject> javaObjects = new LinkedList<>();
final File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
javaObjects.addAll(scanRecursivelyForJavaObjects(file, fileManager));
} else if (file.isFile() && file.getName().toLowerCase().endsWith(".java")) {
try {
LOG.debug(FileUtils.readFileToString(file));
} catch (IOException e) {
LOG.warn("Could not read file", e);
}
javaObjects.add(readJavaObject(file, fileManager));
}
}
}
return javaObjects;
}

private JavaFileObject readJavaObject(final File file, final StandardJavaFileManager fileManager) {
final Iterable<? extends JavaFileObject> javaFileObjects = fileManager.getJavaFileObjects(file);
final Iterator<? extends JavaFileObject> it = javaFileObjects.iterator();
if (it.hasNext()) {
return it.next();
}
throw new CompilationException(format("Could not load {0} java file object", file.getAbsolutePath()));
}

}
Loading

0 comments on commit df92bb3

Please sign in to comment.