Commit 284ea6a3 authored by David Trott's avatar David Trott

Initial commit of code written by Paddy Hannon.

parents
<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>
<groupId>org.apache.thrift.tools</groupId>
<artifactId>maven-thrift-plugin</artifactId>
<packaging>maven-plugin</packaging>
<name>Maven Thrift Plugin</name>
<version>0.1.1-SNAPSHOT</version>
<prerequisites>
<maven>2.0.6</maven>
</prerequisites>
<scm>
<connection>scm:git:git://github.com/dtrott/maven-thrift-plugin.git</connection>
</scm>
<distributionManagement>
<downloadUrl>http://maven.davidtrott.com/repository</downloadUrl>
<repository>
<id>dtrott-public</id>
<name>David Trott's Public Repository</name>
<url>ftp://maven.davidtrott.com/repository</url>
</repository>
<snapshotRepository>
<id>dtrott-public</id>
<name>David Trott's Public Snapshot Repository</name>
<url>ftp://maven.davidtrott.com/snapshot</url>
</snapshotRepository>
</distributionManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
<extensions>
<extension>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-ftp</artifactId>
<version>1.0-beta-6</version>
</extension>
</extensions>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>2.0.9</version>
</dependency>
<dependency>
<groupId>com.google.collections</groupId>
<artifactId>google-collections</artifactId>
<version>0.8</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-project</artifactId>
<version>2.0.9</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.1.0</version>
</dependency>
</dependencies>
</project>
package org.apache.thrift.maven;
import static com.google.common.base.Join.join;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.collect.ImmutableSet;
import static com.google.common.collect.Sets.newHashSet;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import static org.codehaus.plexus.util.FileUtils.cleanDirectory;
import static org.codehaus.plexus.util.FileUtils.copyStreamToFile;
import static org.codehaus.plexus.util.FileUtils.forceDeleteOnExit;
import static org.codehaus.plexus.util.FileUtils.getFiles;
import org.codehaus.plexus.util.cli.CommandLineException;
import org.codehaus.plexus.util.io.RawInputStreamFacade;
import java.io.File;
import java.io.IOException;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import static java.util.Collections.list;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
abstract class AbstractThriftMojo extends AbstractMojo {
private static final String THRIFT_FILE_SUFFIX = ".thrift";
private static final String DEFAULT_INCLUDES = "**/*" + THRIFT_FILE_SUFFIX;
/**
* The current Maven project.
*
* @parameter default-value="${project}"
* @readonly
* @required
*/
protected MavenProject project;
/**
* A helper used to add resources to the project.
*
* @component
* @required
*/
protected MavenProjectHelper projectHelper;
/**
* This is the path to the {@code thrift} executable. By default it will
* search the {@code $PATH}.
*
* @parameter default-value="thrift"
* @required
*/
private String thriftExecutable;
/**
* @parameter
*/
private File[] additionalThriftPathElements = new File[]{};
/**
* Since {@code thrift} cannot access jars, thrift files in dependenceies are
* extracted to this location and deleted on exit. This directory is always
* cleaned during execution.
*
* @parameter expression="${java.io.tmpdir}/maven-thrift"
* @required
*/
private File temporaryThriftFileDirectory;
/**
*
* @parameter
*/
private Set<String> includes = ImmutableSet.of(DEFAULT_INCLUDES);
/**
* @parameter
*/
private Set<String> excludes = ImmutableSet.of();
/**
* Executes the mojo.
*/
public void execute() throws MojoExecutionException, MojoFailureException {
checkParameters();
final File thriftSourceRoot = getThriftSourceRoot();
if (thriftSourceRoot.exists()) {
try {
ImmutableSet<File> thriftFiles =
findThriftFilesInDirectory(thriftSourceRoot);
if (thriftFiles.isEmpty()) {
getLog().info("No thrift files to compile.");
} else {
ImmutableSet<File> derivedThriftPathElements =
makeThriftPathFromJars(
temporaryThriftFileDirectory, getDependencyArtifactFiles());
final File outputDirectory = getOutputDirectory();
outputDirectory.mkdirs();
Thrift thrift =
new Thrift.Builder(thriftExecutable, outputDirectory)
.addThriftPathElement(thriftSourceRoot)
.addThriftPathElements(derivedThriftPathElements)
.addThriftPathElements(asList(additionalThriftPathElements))
.addThriftFiles(thriftFiles)
.build();
final int exitStatus = thrift.compile();
if (exitStatus != 0) {
throw new MojoFailureException(
"Thrift did not exit cleanly. Review output for more information.");
}
attachFiles();
}
} catch (IOException e) {
throw new MojoExecutionException("An IO error occured", e);
} catch (IllegalArgumentException e) {
throw new MojoFailureException("Thrift failed to execute because: "
+ e.getMessage(), e);
} catch (CommandLineException e) {
throw new MojoExecutionException(
"An error occured while invoking thrift.", e);
}
} else {
getLog()
.info(
format(
"%s does not exist. Review the configuration or consider disabling the plugin.",
thriftSourceRoot));
}
}
private void checkParameters() {
checkNotNull(project, "project");
checkNotNull(projectHelper, "projectHelper");
checkNotNull(thriftExecutable, "thriftExecutable");
final File thriftSourceRoot = getThriftSourceRoot();
checkNotNull(thriftSourceRoot);
checkArgument(!thriftSourceRoot.isFile(),
"thriftSourceRoot is a file, not a diretory");
checkNotNull(temporaryThriftFileDirectory, "temporaryThriftFileDirectory");
checkState(!temporaryThriftFileDirectory.isFile(),
"temporaryThriftFileDirectory is a file, not a directory");
final File outputDirectory = getOutputDirectory();
checkNotNull(outputDirectory);
checkState(!outputDirectory.isFile(),
"the outputDirectory is a file, not a directory");
}
protected abstract File getThriftSourceRoot();
protected abstract List<Artifact> getDependencyArtifacts();
protected abstract File getOutputDirectory();
protected abstract void attachFiles();
/**
* Gets the {@link File} for each dependency artifact.
*
* @return A set of all dependency artifacts.
*/
private ImmutableSet<File> getDependencyArtifactFiles() {
Set<File> dependencyArtifactFiles = newHashSet();
for (Artifact artifact : getDependencyArtifacts()) {
dependencyArtifactFiles.add(artifact.getFile());
}
return ImmutableSet.copyOf(dependencyArtifactFiles);
}
/**
*
* @throws IOException
*/
ImmutableSet<File> makeThriftPathFromJars(
File temporaryThriftFileDirectory, Iterable<File> classpathElementFiles)
throws IOException {
checkNotNull(classpathElementFiles, "classpathElementFiles");
// clean the temporary directory to ensure that stale files aren't used
if (temporaryThriftFileDirectory.exists()) {
cleanDirectory(temporaryThriftFileDirectory);
}
Set<File> thriftDirectories = newHashSet();
for (File classpathElementFile : classpathElementFiles) {
checkArgument(classpathElementFile.isFile(), "%s is not a file",
classpathElementFile);
// create the jar file. the constructor validates.
JarFile classpathJar = null;
try {
classpathJar = new JarFile(classpathElementFile);
} catch (IOException e) {
throw new IllegalArgumentException(format(
"%s was not a readable artifact", classpathElementFile));
}
for (JarEntry jarEntry : list(classpathJar.entries())) {
final String jarEntryName = jarEntry.getName();
if (jarEntry.getName().endsWith(THRIFT_FILE_SUFFIX)) {
final File uncompressedCopy =
new File(new File(temporaryThriftFileDirectory, classpathJar
.getName()), jarEntryName);
uncompressedCopy.getParentFile().mkdirs();
copyStreamToFile(new RawInputStreamFacade(classpathJar
.getInputStream(jarEntry)), uncompressedCopy);
thriftDirectories.add(uncompressedCopy.getParentFile());
}
}
}
forceDeleteOnExit(temporaryThriftFileDirectory);
return ImmutableSet.copyOf(thriftDirectories);
}
ImmutableSet<File> findThriftFilesInDirectory(File directory)
throws IOException {
checkNotNull(directory);
checkArgument(directory.isDirectory(), "%s is not a directory", directory);
// TODO(gak): plexus-utils needs generics
@SuppressWarnings("unchecked")
List<File> thriftFilesInDirectory =
getFiles(directory, join(",", includes), join(",", excludes));
return ImmutableSet.copyOf(thriftFilesInDirectory);
}
ImmutableSet<File> findThriftFilesInDirectories(
Iterable<File> directories) throws IOException {
checkNotNull(directories);
Set<File> thriftFiles = newHashSet();
for (File directory : directories) {
thriftFiles.addAll(findThriftFilesInDirectory(directory));
}
return ImmutableSet.copyOf(thriftFiles);
}
}
package org.apache.thrift.maven;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Lists.newLinkedList;
import static com.google.common.collect.Sets.newHashSet;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.codehaus.plexus.util.cli.CommandLineException;
import org.codehaus.plexus.util.cli.CommandLineUtils;
import org.codehaus.plexus.util.cli.Commandline;
import org.codehaus.plexus.util.cli.DefaultConsumer;
import org.codehaus.plexus.util.cli.StreamConsumer;
import java.io.File;
import java.util.List;
import java.util.Set;
/**
* This class represents an invokable configuration of the {@code thrift}
* compiler. The actual executable is invoked using the plexus
* {@link Commandline}.
* <p/>
* This class currently only supports generating java source files.
*
* @author gak@google.com (Gregory Kick)
*/
final class Thrift {
private final String executable;
private final ImmutableSet<File> thriftPathElements;
private final ImmutableSet<File> thriftFiles;
private final File javaOutputDirectory;
/**
* Constructs a new instance. This should only be used by the {@link Builder}.
*
* @param executable The path to the {@code thrift} executable.
* @param thriftPath The directories in which to search for imports.
* @param thriftFiles The thrift source files to compile.
* @param javaOutputDirectory The directory into which the java source files
* will be generated.
*/
private Thrift(String executable, ImmutableSet<File> thriftPath,
ImmutableSet<File> thriftFiles, File javaOutputDirectory) {
this.executable = checkNotNull(executable, "executable");
this.thriftPathElements = checkNotNull(thriftPath, "thriftPath");
this.thriftFiles = checkNotNull(thriftFiles, "thriftFiles");
this.javaOutputDirectory =
checkNotNull(javaOutputDirectory, "javaOutputDirectory");
}
/**
* Invokes the {@code thrift} compiler using the configuration specified at
* construction.
*
* @return The exit status of {@code thrift}.
* @throws CommandLineException
*/
public int compile() throws CommandLineException {
Commandline cl = new Commandline(executable);
cl.addArguments(buildThriftCommand().toArray(new String[]{}));
StreamConsumer output = new DefaultConsumer();
StreamConsumer error = new DefaultConsumer();
return CommandLineUtils.executeCommandLine(cl, null, output, error);
}
/**
* Creates the command line arguments.
* <p/>
* This method has been made visible for testing only.
*
* @return A list consisting of the executable followed by any arguments.
*/
ImmutableList<String> buildThriftCommand() {
final List<String> command = newLinkedList();
// add the executable
for (File thriftPathElement : thriftPathElements) {
command.add("-I");
command.add(thriftPathElement.toString());
}
command.add("-o");
command.add(javaOutputDirectory.toString());
command.add("--gen");
command.add("java");
for (File thriftFile : thriftFiles) {
command.add(thriftFile.toString());
}
return ImmutableList.copyOf(command);
}
/**
* This class builds {@link Thrift} instances.
*
* @author gak@google.com (Gregory Kick)
*/
static final class Builder {
private final String executable;
private final File javaOutputDirectory;
private Set<File> thriftPathElements;
private Set<File> thriftFiles;
/**
* Constructs a new builder. The two parameters are present as they are
* required for all {@link Thrift} instances.
*
* @param executable The path to the {@code thrift} executable.
* @param javaOutputDirectory The directory into which the java source files
* will be generated.
* @throws NullPointerException If either of the arguments are {@code null}.
* @throws IllegalArgumentException If the {@code javaOutputDirectory} is
* not a directory.
*/
public Builder(String executable, File javaOutputDirectory) {
this.executable = checkNotNull(executable, "executable");
this.javaOutputDirectory = checkNotNull(javaOutputDirectory);
checkArgument(javaOutputDirectory.isDirectory());
this.thriftFiles = newHashSet();
this.thriftPathElements = newHashSet();
}
/**
* Adds a thrift file to be compiled. Thrift files must be on the thriftpath
* and this method will fail if a thrift file is added without first adding a
* parent directory to the thriftpath.
*
* @param thriftFile
* @return The builder.
* @throws IllegalStateException If a thrift file is added without first
* adding a parent directory to the thriftpath.
* @throws NullPointerException If {@code thriftFile} is {@code null}.
*/
public Builder addThriftFile(File thriftFile) {
checkNotNull(thriftFile);
checkArgument(thriftFile.isFile());
checkArgument(thriftFile.getName().endsWith(".thrift"));
checkThriftFileIsInThriftPath(thriftFile);
thriftFiles.add(thriftFile);
return this;
}
private void checkThriftFileIsInThriftPath(File thriftFile) {
assert thriftFile.isFile();
checkState(checkThriftFileIsInThriftPathHelper(thriftFile.getParentFile()));
}
private boolean checkThriftFileIsInThriftPathHelper(File directory) {
assert directory.isDirectory();
if (thriftPathElements.contains(directory)) {
return true;
} else {
final File parentDirectory = directory.getParentFile();
return (parentDirectory == null) ? false
: checkThriftFileIsInThriftPathHelper(parentDirectory);
}
}
/**
* @see #addThriftFile(File)
*/
public Builder addThriftFiles(Iterable<File> thriftFiles) {
for (File thriftFile : thriftFiles) {
addThriftFile(thriftFile);
}
return this;
}
/**
* Adds the {@code thriftPathElement} to the thriftPath.
*
* @param thriftPathElement A directory to be searched for imported thrift message
* buffer definitions.
* @return The builder.
* @throws NullPointerException If {@code thriftPathElement} is {@code null}.
* @throws IllegalArgumentException If {@code thriftPathElement} is not a
* directory.
*/
public Builder addThriftPathElement(File thriftPathElement) {
checkNotNull(thriftPathElement);
checkArgument(thriftPathElement.isDirectory());
thriftPathElements.add(thriftPathElement);
return this;
}
/**
* @see #addThriftPathElement(File)
*/
public Builder addThriftPathElements(Iterable<File> thriftPathElements) {
for (File thriftPathElement : thriftPathElements) {
addThriftPathElement(thriftPathElement);
}
return this;
}
/**
* @return A configured {@link Thrift} instance.
* @throws IllegalStateException If no thrift files have been added.
*/
public Thrift build() {
checkState(!thriftFiles.isEmpty());
return new Thrift(executable, ImmutableSet.copyOf(thriftPathElements),
ImmutableSet.copyOf(thriftFiles), javaOutputDirectory);
}
}
}
package org.apache.thrift.maven;
import com.google.common.collect.ImmutableList;
import org.apache.maven.artifact.Artifact;
import java.io.File;
import java.util.List;
/**
* This mojo executes the {@code thrift} compiler for generating java sources
* from thrift definitions. It also searches dependency artifacts for
* thrift files and includes them in the thriftPath so that they can be
* referenced. Finally, it adds the thrift files to the project as resources so
* that they are included in the final artifact.
*
* @phase generate-sources
* @goal compile
* @requiresDependencyResolution compile
*/
public final class ThriftCompileMojo extends AbstractThriftMojo {
/**
* The source directories containing the sources to be compiled.
*
* @parameter default-value="${basedir}/src/main/thrift"
* @required
*/
private File thriftSourceRoot;
/**
* This is the directory into which the {@code .java} will be created.
*
* @parameter default-value="${project.build.directory}/generated-sources/thrift"
* @required
*/
private File outputDirectory;
@Override
protected List<Artifact> getDependencyArtifacts() {
// TODO(gak): maven-project needs generics
@SuppressWarnings("unchecked")
List<Artifact> compileArtifacts = project.getCompileArtifacts();
return compileArtifacts;
}
@Override
protected File getOutputDirectory() {
return outputDirectory;
}
@Override
protected File getThriftSourceRoot() {
return thriftSourceRoot;
}
@Override
protected void attachFiles() {
project.addCompileSourceRoot(outputDirectory.getAbsolutePath());
projectHelper.addResource(project, thriftSourceRoot.getAbsolutePath(),
ImmutableList.of("**/*.thrift"), ImmutableList.of());
}
}
package org.apache.thrift.maven;
import com.google.common.collect.ImmutableList;
import org.apache.maven.artifact.Artifact;
import java.io.File;
import java.util.List;
/**
*
* @phase generate-test-sources
* @goal testCompile
* @requiresDependencyResolution test
*/
public final class ThriftTestCompileMojo extends AbstractThriftMojo {
/**
* The source directories containing the sources to be compiled.
*
* @parameter default-value="${basedir}/src/test/thrift"
* @required
*/
private File thriftTestSourceRoot;
/**
* This is the directory into which the {@code .java} will be created.
*
* @parameter default-value="${project.build.directory}/generated-test-sources/thrift"
* @required
*/
private File outputDirectory;
@Override
protected void attachFiles() {
project.addTestCompileSourceRoot(outputDirectory.getAbsolutePath());
projectHelper.addTestResource(project, thriftTestSourceRoot.getAbsolutePath(),
ImmutableList.of("**/*.thrift"), ImmutableList.of());
}
@Override
protected List<Artifact> getDependencyArtifacts() {
// TODO(gak): maven-project needs generics
@SuppressWarnings("unchecked")
List<Artifact> testArtifacts = project.getTestArtifacts();
return testArtifacts;
}
@Override
protected File getOutputDirectory() {
return outputDirectory;
}
@Override
protected File getThriftSourceRoot() {
return thriftTestSourceRoot;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment