// Copyright © 2011-2012, Esko Luontola <www.orfjackal.net>
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0
package fi.jumi.actors.maven;
import fi.jumi.actors.maven.codegen.GeneratedClass;
import org.apache.commons.io.FileUtils;
import org.apache.maven.plugin.*;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.compiler.*;
import org.codehaus.plexus.compiler.javac.JavacCompiler;
import java.io.*;
import java.net.*;
import java.util.*;
/**
* @goal generate-event-stubs
* @phase generate-sources
* @requiresDependencyResolution compile
* @threadSafe
*/
public class GenerateEventStubsMojo extends AbstractMojo {
// TODO: is there a way to get these source and target parameters from the maven-compiler-plugin's configuration?
/**
* The -source argument for the Java compiler.
*
* @parameter property="maven.compiler.source" default-value="1.6"
* @required
*/
public String source;
/**
* The -target argument for the Java compiler.
*
* @parameter property="maven.compiler.target" default-value="1.6"
* @required
*/
public String target;
/**
* @parameter default-value="${project.build.sourceEncoding}"
* @required
*/
public String encoding;
/**
* @parameter default-value="${project.build.sourceDirectory}"
* @required
*/
public File sourceDirectory;
/**
* @parameter default-value="${project.build.directory}/generated-sources/jumi-actors"
* @required
*/
public File outputDirectory;
/**
* @parameter default-value="${project.compileClasspathElements}"
* @required
* @readonly
*/
private String[] projectClasspath;
/**
* @parameter default-value="${project.build.directory}/jumi-actors"
* @required
*/
public File tempDirectory;
/**
* @parameter
* @required
*/
public String targetPackage;
/**
* @parameter
* @required
*/
public String[] eventInterfaces;
/**
* The Maven Project Object
*
* @parameter property="project"
* @required
* @readonly
*/
protected MavenProject project;
@Override
public void execute() throws MojoExecutionException {
TargetPackageResolver targetPackageResolver = new TargetPackageResolver(targetPackage);
for (String eventInterface : eventInterfaces) {
EventStubGenerator generator = new EventStubGenerator(loadClass(eventInterface), targetPackageResolver);
List<GeneratedClass> generated = new ArrayList<GeneratedClass>();
generated.add(generator.getEventizer());
generated.add(generator.getFrontend());
generated.add(generator.getBackend());
generated.addAll(generator.getEvents());
for (GeneratedClass c : generated) {
try {
FileUtils.write(new File(outputDirectory, c.path), c.source, encoding);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
project.addCompileSourceRoot(outputDirectory.getAbsolutePath());
}
}
private Class<?> loadClass(String eventInterface) throws MojoExecutionException {
try {
return loadFromClasspath(eventInterface, toFiles(projectClasspath));
} catch (ClassNotFoundException e) {
// the class is part of this project and is still a source file
}
compileClass(eventInterface, tempDirectory);
List<File> classpath = new ArrayList<File>();
classpath.add(tempDirectory);
classpath.addAll(Arrays.asList(toFiles(projectClasspath)));
try {
return loadFromClassPath(eventInterface, classpath);
} catch (ClassNotFoundException e) {
throw new MojoExecutionException("Could not load event interface: " + eventInterface, e);
}
}
private void compileClass(String classToCompile, File targetDir) throws MojoExecutionException {
HashSet<String> includes = new HashSet<String>();
includes.add(classToCompile.replace('.', '/') + ".java");
CompilerConfiguration config = new CompilerConfiguration();
if (sourceDirectory.isDirectory()) {
config.addSourceLocation(sourceDirectory.getAbsolutePath());
}
config.setIncludes(includes);
config.setOutputLocation(targetDir.getAbsolutePath());
config.setSourceEncoding(encoding);
config.setSourceVersion(source);
config.setTargetVersion(target);
config.setClasspathEntries(Arrays.asList(projectClasspath));
try {
JavacCompiler javac = new JavacCompiler();
getLog().debug("Compiling event interface using: " + Arrays.toString(javac.createCommandLine(config)));
List<CompilerError> messages = javac.compile(config);
checkForCompilerErrors(messages, classToCompile);
} catch (CompilerException e) {
throw new MojoExecutionException("Cannot compile event interface: " + classToCompile, e);
}
}
private static Class<?> loadFromClassPath(String className, List<File> classpath) throws ClassNotFoundException {
return loadFromClasspath(className, classpath.toArray(new File[classpath.size()]));
}
private static Class<?> loadFromClasspath(String className, File[] classpath) throws ClassNotFoundException {
try {
URL[] urls = FileUtils.toURLs(classpath);
URLClassLoader loader = new URLClassLoader(urls);
return loader.loadClass(className);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static void checkForCompilerErrors(List<CompilerError> messages, String eventInterface) throws MojoExecutionException {
if (!messages.isEmpty()) {
String compilerMessages = "";
boolean hadErrors = false;
for (CompilerError message : messages) {
hadErrors |= message.isError();
compilerMessages += "\n" + message;
}
if (hadErrors) {
throw new MojoExecutionException("There were compiler errors when compiling the event interface: "
+ eventInterface + compilerMessages);
}
}
}
private static File[] toFiles(String[] paths) {
File[] files = new File[paths.length];
for (int i = 0; i < paths.length; i++) {
files[i] = new File(paths[i]);
}
return files;
}
}