blob: cbac38c07459f396a8e4b3e52f82bbc85fcf7bba [file] [log] [blame]
/*
* 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
*
* https://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.avro.mojo;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.avro.compiler.specific.SpecificCompiler;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.model.fileset.FileSet;
import org.apache.maven.shared.model.fileset.util.FileSetManager;
/**
* Base for Avro Compiler Mojos.
*/
public abstract class AbstractAvroMojo extends AbstractMojo {
/**
* The source directory of avro files. This directory is added to the classpath
* at schema compiling time. All files can therefore be referenced as classpath
* resources following the directory structure under the source directory.
*
* @parameter property="sourceDirectory"
* default-value="${basedir}/src/main/avro"
*/
private File sourceDirectory;
/**
* @parameter property="outputDirectory"
* default-value="${project.build.directory}/generated-sources/avro"
*/
private File outputDirectory;
/**
* @parameter property="sourceDirectory"
* default-value="${basedir}/src/test/avro"
*/
private File testSourceDirectory;
/**
* @parameter property="outputDirectory"
* default-value="${project.build.directory}/generated-test-sources/avro"
*/
private File testOutputDirectory;
/**
* The field visibility indicator for the fields of the generated class, as
* string values of SpecificCompiler.FieldVisibility. The text is case
* insensitive.
*
* @parameter default-value="PRIVATE"
*/
private String fieldVisibility;
/**
* A list of files or directories that should be compiled first thus making them
* importable by subsequently compiled schemas. Note that imported files should
* not reference each other.
*
* @parameter
*/
protected String[] imports;
/**
* A set of Ant-like exclusion patterns used to prevent certain files from being
* processed. By default, this set is empty such that no files are excluded.
*
* @parameter
*/
protected String[] excludes = new String[0];
/**
* A set of Ant-like exclusion patterns used to prevent certain files from being
* processed. By default, this set is empty such that no files are excluded.
*
* @parameter
*/
protected String[] testExcludes = new String[0];
/**
* The Java type to use for Avro strings. May be one of CharSequence, String or
* Utf8. CharSequence by default.
*
* @parameter property="stringType"
*/
protected String stringType = "CharSequence";
/**
* The directory (within the java classpath) that contains the velocity
* templates to use for code generation. The default value points to the
* templates included with the avro-maven-plugin.
*
* @parameter property="templateDirectory"
*/
protected String templateDirectory = "/org/apache/avro/compiler/specific/templates/java/classic/";
/**
* The qualified names of classes which the plugin will look up, instantiate
* (through an empty constructor that must exist) and set up to be injected into
* Velocity templates by Avro compiler.
*
* @parameter property="velocityToolsClassesNames"
*/
protected String[] velocityToolsClassesNames = new String[0];
/**
* The createOptionalGetters parameter enables generating the getOptional...
* methods that return an Optional of the requested type. This works ONLY on
* Java 8+
*
* @parameter property="createOptionalGetters"
*/
protected boolean createOptionalGetters = false;
/**
* The gettersReturnOptional parameter enables generating get... methods that
* return an Optional of the requested type. This will replace the This works
* ONLY on Java 8+
*
* @parameter property="gettersReturnOptional"
*/
protected boolean gettersReturnOptional = false;
/**
* The optionalGettersForNullableFieldsOnly parameter works in conjunction with
* gettersReturnOptional option. If it is set, Optional getters will be
* generated only for fields that are nullable. If the field is mandatory,
* regular getter will be generated. This works ONLY on Java 8+.
*
* @parameter property="optionalGettersForNullableFieldsOnly"
*/
protected boolean optionalGettersForNullableFieldsOnly = false;
/**
* Determines whether or not to create setters for the fields of the record. The
* default is to create setters.
*
* @parameter default-value="true"
*/
protected boolean createSetters;
/**
* A set of fully qualified class names of custom
* {@link org.apache.avro.Conversion} implementations to add to the compiler.
* The classes must be on the classpath at compile time and whenever the Java
* objects are serialized.
*
* @parameter property="customConversions"
*/
protected String[] customConversions = new String[0];
/**
* Determines whether or not to use Java classes for decimal types
*
* @parameter default-value="false"
*/
protected boolean enableDecimalLogicalType;
/**
* The current Maven project.
*
* @parameter default-value="${project}"
* @readonly
* @required
*/
protected MavenProject project;
@Override
public void execute() throws MojoExecutionException {
boolean hasSourceDir = null != sourceDirectory && sourceDirectory.isDirectory();
boolean hasImports = null != imports;
boolean hasTestDir = null != testSourceDirectory && testSourceDirectory.isDirectory();
if (!hasSourceDir && !hasTestDir) {
throw new MojoExecutionException("neither sourceDirectory: " + sourceDirectory + " or testSourceDirectory: "
+ testSourceDirectory + " are directories");
}
if (hasImports) {
for (String importedFile : imports) {
File file = new File(importedFile);
if (file.isDirectory()) {
String[] includedFiles = getIncludedFiles(file.getAbsolutePath(), excludes, getIncludes());
getLog().info("Importing Directory: " + file.getAbsolutePath());
getLog().debug("Importing Directory Files: " + Arrays.toString(includedFiles));
compileFiles(includedFiles, file, outputDirectory);
} else if (file.isFile()) {
getLog().info("Importing File: " + file.getAbsolutePath());
compileFiles(new String[] { file.getName() }, file.getParentFile(), outputDirectory);
}
}
}
if (hasSourceDir) {
String[] includedFiles = getIncludedFiles(sourceDirectory.getAbsolutePath(), excludes, getIncludes());
compileFiles(includedFiles, sourceDirectory, outputDirectory);
}
if (hasImports || hasSourceDir) {
project.addCompileSourceRoot(outputDirectory.getAbsolutePath());
}
if (hasTestDir) {
String[] includedFiles = getIncludedFiles(testSourceDirectory.getAbsolutePath(), testExcludes, getTestIncludes());
compileFiles(includedFiles, testSourceDirectory, testOutputDirectory);
project.addTestCompileSourceRoot(testOutputDirectory.getAbsolutePath());
}
}
private String[] getIncludedFiles(String absPath, String[] excludes, String[] includes) {
final FileSetManager fileSetManager = new FileSetManager();
final FileSet fs = new FileSet();
fs.setDirectory(absPath);
fs.setFollowSymlinks(false);
// exclude imports directory since it has already been compiled.
if (imports != null) {
String importExclude = null;
for (String importFile : this.imports) {
File file = new File(importFile);
if (file.isDirectory()) {
importExclude = file.getName() + "/**";
} else if (file.isFile()) {
importExclude = "**/" + file.getName();
}
fs.addExclude(importExclude);
}
}
for (String include : includes) {
fs.addInclude(include);
}
for (String exclude : excludes) {
fs.addExclude(exclude);
}
return fileSetManager.getIncludedFiles(fs);
}
private void compileFiles(String[] files, File sourceDir, File outDir) throws MojoExecutionException {
for (String filename : files) {
try {
doCompile(filename, sourceDir, outDir);
} catch (IOException e) {
throw new MojoExecutionException("Error compiling protocol file " + filename + " to " + outDir, e);
}
}
}
protected SpecificCompiler.FieldVisibility getFieldVisibility() {
try {
String upper = String.valueOf(this.fieldVisibility).trim().toUpperCase();
return SpecificCompiler.FieldVisibility.valueOf(upper);
} catch (IllegalArgumentException e) {
return SpecificCompiler.FieldVisibility.PRIVATE;
}
}
protected List<Object> instantiateAdditionalVelocityTools() {
final List<Object> velocityTools = new ArrayList<>(velocityToolsClassesNames.length);
for (String velocityToolClassName : velocityToolsClassesNames) {
try {
Class klass = Class.forName(velocityToolClassName);
velocityTools.add(klass.newInstance());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return velocityTools;
}
protected abstract void doCompile(String filename, File sourceDirectory, File outputDirectory) throws IOException;
protected URLClassLoader createClassLoader() throws DependencyResolutionRequiredException, MalformedURLException {
final List<URL> urls = appendElements(project.getRuntimeClasspathElements());
urls.addAll(appendElements(project.getTestClasspathElements()));
return new URLClassLoader(urls.toArray(new URL[0]), Thread.currentThread().getContextClassLoader());
}
private List<URL> appendElements(List runtimeClasspathElements) throws MalformedURLException {
if (runtimeClasspathElements == null) {
return new ArrayList<>();
}
List<URL> runtimeUrls = new ArrayList<>(runtimeClasspathElements.size());
for (Object runtimeClasspathElement : runtimeClasspathElements) {
String element = (String) runtimeClasspathElement;
runtimeUrls.add(new File(element).toURI().toURL());
}
return runtimeUrls;
}
protected abstract String[] getIncludes();
protected abstract String[] getTestIncludes();
}