From 06df2577504adf062287b58b162218db8880754c Mon Sep 17 00:00:00 2001 From: Rafael Winterhalter Date: Wed, 31 Aug 2016 15:19:43 +0200 Subject: [PATCH] Added documentation. --- .../gradle/AbstractUserConfiguration.java | 87 ++++++++++++- .../build/gradle/ByteBuddyExtension.java | 116 ++++++++++++++++-- .../build/gradle/ByteBuddyPlugin.java | 3 + .../build/gradle/ClassLoaderResolver.java | 53 ++++++-- .../build/gradle/Initialization.java | 23 +++- .../build/gradle/PostCompilationAction.java | 31 ++++- .../build/gradle/Transformation.java | 21 ++++ .../build/gradle/TransformationAction.java | 74 +++++++++-- .../bytebuddy/build/gradle/package-info.java | 4 + .../bytebuddy/build/maven/Transformation.java | 2 +- 10 files changed, 379 insertions(+), 35 deletions(-) create mode 100644 byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/package-info.java diff --git a/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/AbstractUserConfiguration.java b/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/AbstractUserConfiguration.java index cdba9a4155a..4f6bd3173f5 100644 --- a/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/AbstractUserConfiguration.java +++ b/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/AbstractUserConfiguration.java @@ -3,27 +3,58 @@ import java.io.File; import java.util.Iterator; +/** + * An abstract base class for a user configuration implying a class path. + */ public class AbstractUserConfiguration { + /** + * The class to use for this configuration path or {@code null} if no class path is specified. + */ private Iterable classPath; + /** + * Returns the class path or builds a class path from the supplied arguments if no class path was set. + * + * @param root The root directory of the project being built. + * @param classPath The class path dependencies. + * @return An iterable of all elements of the class path to be used. + */ public Iterable getClassPath(File root, Iterable classPath) { return this.classPath == null ? new PrefixIterable(root, classPath) : this.classPath; } + /** + * Sets the class path to use for this configuration. + * + * @param classPath The class path to use. + */ public void setClassPath(Iterable classPath) { this.classPath = classPath; } + /** + * An iterable with a single {@link File} element prepended. + */ protected static class PrefixIterable implements Iterable { + /** + * The prefixed file. + */ private final File file; + /** + * The iterable containing the reminder files. + */ private final Iterable files; - public PrefixIterable(File file, Iterable files) { + /** + * @param file The prefixed file. + * @param files The iterable containing the reminder files. + */ + protected PrefixIterable(File file, Iterable files) { this.file = file; this.files = files; } @@ -33,15 +64,56 @@ public Iterator iterator() { return new PrefixIterator(file, files.iterator()); } + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + PrefixIterable files1 = (PrefixIterable) object; + return file.equals(files1.file) && files.equals(files1.files); + } + + @Override + public int hashCode() { + int result = file.hashCode(); + result = 31 * result + files.hashCode(); + return result; + } + + @Override + public String toString() { + return "AbstractUserConfiguration.PrefixIterable{" + + "file=" + file + + ", files=" + files + + '}'; + } + + /** + * An iterator with a single prefixed file. + */ protected static class PrefixIterator implements Iterator { + /** + * The file being prefixed. + */ private final File file; + /** + * An iterator over the reminind files. + */ private final Iterator files; + /** + * {@code true} if the prefix was not yet returned from the iteration. + */ private boolean first; - public PrefixIterator(File file, Iterator files) { + /** + * Creates a prefix iterator. + * + * @param file The file being prefixed. + * @param files An iterator over the reminind files. + */ + protected PrefixIterator(File file, Iterator files) { this.file = file; this.files = files; first = true; @@ -64,7 +136,16 @@ public File next() { @Override public void remove() { - throw new UnsupportedOperationException("Cannot remove from iterator"); + throw new UnsupportedOperationException("Cannot remove file from iterator"); + } + + @Override + public String toString() { + return "AbstractUserConfiguration.PrefixIterable.PrefixIterator{" + + "file=" + file + + ", files=" + files + + ", first=" + first + + '}'; } } } diff --git a/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/ByteBuddyExtension.java b/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/ByteBuddyExtension.java index ef84a79e906..c506ab094a7 100644 --- a/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/ByteBuddyExtension.java +++ b/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/ByteBuddyExtension.java @@ -1,66 +1,162 @@ package net.bytebuddy.build.gradle; import groovy.lang.Closure; +import net.bytebuddy.dynamic.scaffold.inline.MethodNameTransformer; import org.gradle.api.GradleException; import org.gradle.api.Project; +import org.gradle.api.Task; import java.util.ArrayList; import java.util.List; +/** + * Representation of a Gradle configuration for Byte Buddy. + */ public class ByteBuddyExtension { + /** + * The current project. + */ private final Project project; - private List transformations; + /** + * A list of registered transformations. + */ + private final List transformations; + /** + * The initialization or {@code null} if no initialization was defined by a user. + */ private Initialization initialization; + /** + * The suffix to use for rebased methods or {@code null} if a random suffix should be used. + */ private String suffix; + /** + * {@code true} if the plugin should fail upon discovering a live runtime initializer. + */ private boolean failOnLiveInitializer; + private List tasks; + + /** + * Creates a new Byte Buddy extension. + * + * @param project The current project. + */ public ByteBuddyExtension(Project project) { this.project = project; transformations = new ArrayList(); failOnLiveInitializer = true; } - public Transformation transformation(Closure closure) { + /** + * Adds a transformation to apply. + * + * @param closure The closure for configuring the transformation. + */ + public void transformation(Closure closure) { Transformation transformation = (Transformation) project.configure(new Transformation(), closure); transformations.add(transformation); - return transformation; } - public Initialization initialization(Closure closure) { + /** + * Adds an initialization to apply. + * + * @param closure The closure for configuring the initialization. + */ + public void initialization(Closure closure) { if (initialization != null) { throw new GradleException("Initialization is already set"); } - Initialization initialization = (Initialization) project.configure(new Initialization(), closure); - this.initialization = initialization; - return initialization; + initialization = (Initialization) project.configure(new Initialization(), closure); } + /** + * Returns a list of transformations to apply. + * + * @return A list of transformations to apply. + */ public List getTransformations() { return transformations; } + /** + * Returns the initialization to use. + * + * @return The initialization to use. + */ public Initialization getInitialization() { - return initialization == null ? Initialization.makeDefault() : initialization; + return initialization == null + ? Initialization.makeDefault() + : initialization; } - public String getSuffix() { - return suffix; + /** + * Returns the method name transformer to use. + * + * @return The method name transformer to use. + */ + public MethodNameTransformer getMethodNameTransformer() { + return suffix == null || suffix.isEmpty() + ? MethodNameTransformer.Suffixing.withRandomSuffix() + : new MethodNameTransformer.Suffixing(suffix); } + /** + * Sets the suffix to apply upon rebased methods. + * + * @param suffix The suffix to apply upon rebased methods. + */ public void setSuffix(String suffix) { this.suffix = suffix; } + /** + * Returns {@code true} if the build should fail upon discovering a live runtime initializer. + * + * @return {@code true} if the build should fail upon discovering a live runtime initializer. + */ public boolean isFailOnLiveInitializer() { return failOnLiveInitializer; } + /** + * Determines if the build should fail upon discovering a live runtime initializer. + * + * @param failOnLiveInitializer {@code true} if the build should fail upon discovering a live runtime initializer. + */ public void setFailOnLiveInitializer(boolean failOnLiveInitializer) { this.failOnLiveInitializer = failOnLiveInitializer; } + + /** + * Sets the initialization that should be used. + * + * @param initialization The initialization to be used. + */ + public void setInitialization(Initialization initialization) { + this.initialization = initialization; + } + + /** + * Determines if a task is subject to transformation. + * + * @param task The task to consider. + * @return {@code true} if this task should be followed up by a transformation. + */ + public boolean implies(Task task) { + return tasks == null || tasks.contains(task.getName()); + } + + /** + * Sets an explicit list of tasks for which a transformation should be applied. + * + * @param tasks The tasks to explicitly append a transformation to. + */ + public void setTasks(List tasks) { + this.tasks = tasks; + } } diff --git a/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/ByteBuddyPlugin.java b/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/ByteBuddyPlugin.java index ddbdd648047..d3d10fae152 100644 --- a/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/ByteBuddyPlugin.java +++ b/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/ByteBuddyPlugin.java @@ -4,6 +4,9 @@ import org.gradle.api.Project; import org.gradle.api.tasks.compile.AbstractCompile; +/** + * A Byte Buddy plugin that appends transformations to all compilation tasks. + */ public class ByteBuddyPlugin implements Plugin { @Override diff --git a/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/ClassLoaderResolver.java b/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/ClassLoaderResolver.java index 3c6211ad467..48d079f0ed8 100644 --- a/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/ClassLoaderResolver.java +++ b/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/ClassLoaderResolver.java @@ -9,28 +9,48 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; +/** + * A class loader resolver for creating class loaders for given class paths. + */ public class ClassLoaderResolver implements Closeable { - private final Map, ClassLoader> classLoaders; + /** + * A mapping of files collections to previously created class loaders. + */ + private final Map, ClassLoader> classLoaders; - public ClassLoaderResolver() throws MalformedURLException { - this.classLoaders = new HashMap, ClassLoader>(); + /** + * Creates a new class loader resolver. + */ + public ClassLoaderResolver() { + classLoaders = new HashMap, ClassLoader>(); } + /** + * Resolves a class path to a class loader. If a class loader for the same file collection was created + * previously, the previous class loader is returned. + * + * @param classPath The class path to consider. + * @return A class loader for the supplied class path. + */ public ClassLoader resolve(Iterable classPath) { - List classPathList = new ArrayList(); + Set classPathList = new LinkedHashSet(); for (File file : classPath) { classPathList.add(file); } return resolve(classPathList); } - protected ClassLoader resolve(List classPath) { + /** + * Resolves a class path to a class loader. If a class loader for the same file collection was created + * previously, the previous class loader is returned. + * + * @param classPath The class path to consider. + * @return A class loader for the supplied class path. + */ + private ClassLoader resolve(Set classPath) { ClassLoader classLoader = classLoaders.get(classPath); if (classLoader == null) { classLoader = doResolve(classPath); @@ -39,7 +59,13 @@ protected ClassLoader resolve(List classPath) { return classLoader; } - private ClassLoader doResolve(List classPath) { + /** + * Resolves a class path to a class loader. + * + * @param classPath The class path to consider. + * @return A class loader for the supplied class path. + */ + private ClassLoader doResolve(Set classPath) { List urls = new ArrayList(classPath.size()); for (File file : classPath) { try { @@ -59,4 +85,11 @@ public void close() throws IOException { } } } + + @Override + public String toString() { + return "ClassLoaderResolver{" + + "classLoaders=" + classLoaders + + '}'; + } } diff --git a/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/Initialization.java b/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/Initialization.java index 3ea7aa91f99..17a13130033 100644 --- a/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/Initialization.java +++ b/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/Initialization.java @@ -6,22 +6,43 @@ import java.io.File; /** - * Defines a configuration for a Maven build's type transformation. + * Defines an entry point for a Byte Buddy transformation in a Gradle build. */ public class Initialization extends AbstractUserConfiguration { + /** + * The fully-qualified name of the entry point or any constant name of {@link EntryPoint.Default}. + */ private String entryPoint; + /** + * Creates a default initialization instance. + * + * @return A default initialization instance. + */ public static Initialization makeDefault() { Initialization initialization = new Initialization(); initialization.setEntryPoint(EntryPoint.Default.REBASE.name()); return initialization; } + /** + * Sets the default entry point or any constant name of {@link EntryPoint.Default}. + * + * @param entryPoint The default entry point or any constant name of {@link EntryPoint.Default}. + */ public void setEntryPoint(String entryPoint) { this.entryPoint = entryPoint; } + /** + * Resolves this initialization to an entry point instance. + * + * @param classLoaderResolver The class loader resolver to use if appropriate. + * @param root The root file describing the current tasks classes. + * @param classPath The class path of the current task. + * @return A resolved entry point. + */ public EntryPoint toEntryPoint(ClassLoaderResolver classLoaderResolver, File root, Iterable classPath) { if (entryPoint == null || entryPoint.isEmpty()) { throw new GradleException("Entry point name is not defined"); diff --git a/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/PostCompilationAction.java b/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/PostCompilationAction.java index 589e1412cf7..11a3d48f6ce 100644 --- a/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/PostCompilationAction.java +++ b/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/PostCompilationAction.java @@ -4,23 +4,48 @@ import org.gradle.api.Project; import org.gradle.api.tasks.compile.AbstractCompile; +/** + * A compilation action that applies a class file transformation after a compilation task. + */ public class PostCompilationAction implements Action { + /** + * The current project. + */ private final Project project; + /** + * The Byte Buddy extension of this build. + */ private final ByteBuddyExtension byteBuddyExtension; - protected PostCompilationAction(Project project, ByteBuddyExtension byteBuddyExtension) { + /** + * Createsa a new post compilation action. + * + * @param project The current project. + * @param byteBuddyExtension The Byte Buddy extension of this build. + */ + public PostCompilationAction(Project project, ByteBuddyExtension byteBuddyExtension) { this.project = project; this.byteBuddyExtension = byteBuddyExtension; } + /** + * Creates a post compilation action. + * + * @param project The project to apply the action upon. + * @return An appropriate action. + */ public static Action of(Project project) { return new PostCompilationAction(project, project.getExtensions().create("byteBuddy", ByteBuddyExtension.class, project)); } @Override - public void execute(AbstractCompile compileTask) { - compileTask.doLast(new TransformationAction(project, byteBuddyExtension, compileTask)); + public void execute(AbstractCompile task) { + if (byteBuddyExtension.implies(task)) { + task.doLast(new TransformationAction(project, byteBuddyExtension, task)); + } else { + project.getLogger().info("Skipping {}", task.getName()); + } } } \ No newline at end of file diff --git a/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/Transformation.java b/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/Transformation.java index e3a454d68e2..6cbb779ee61 100644 --- a/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/Transformation.java +++ b/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/Transformation.java @@ -2,10 +2,21 @@ import org.gradle.api.GradleException; +/** + * A transformation specification to apply during the Gradle plugin's execution. + */ public class Transformation extends AbstractUserConfiguration { + /** + * The fully-qualified name of the plugin type. + */ private String plugin; + /** + * Returns the plugin type name. + * + * @return The plugin type name. + */ public String getPlugin() { if (plugin == null || plugin.isEmpty()) { throw new GradleException("Plugin name was not specified or is empty"); @@ -13,10 +24,20 @@ public String getPlugin() { return plugin; } + /** + * Returns the plugin name or {@code null} if it is not set. + * + * @return The configured plugin name. + */ public String getRawPlugin() { return plugin; } + /** + * Sets the plugin's name. + * + * @param plugin The fully-qualified name of the plugin type. + */ public void setPlugin(String plugin) { this.plugin = plugin; } diff --git a/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/TransformationAction.java b/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/TransformationAction.java index 9df5c3d3b98..c1602ce0417 100644 --- a/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/TransformationAction.java +++ b/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/TransformationAction.java @@ -21,31 +21,60 @@ import java.util.List; import java.util.Map; +/** + * Applies a transformation to the classes that were generated by a compilation task. + */ public class TransformationAction implements Action { + /** + * The class file extension for Java files. + */ private static final String CLASS_FILE_EXTENSION = ".class"; + /** + * The current project. + */ private final Project project; + /** + * The current project's Byte Buddy extension. + */ private final ByteBuddyExtension byteBuddyExtension; - private final AbstractCompile compileTask; + /** + * The task to which this transformation was appended. + */ + private final AbstractCompile task; - public TransformationAction(Project project, ByteBuddyExtension extension, AbstractCompile compileTask) { + /** + * Creates a new transformation action. + * + * @param project The current project. + * @param extension The current project's Byte Buddy extension. + * @param task The task to which this transformation was appended. + */ + public TransformationAction(Project project, ByteBuddyExtension extension, AbstractCompile task) { this.project = project; this.byteBuddyExtension = extension; - this.compileTask = compileTask; + this.task = task; } @Override public void execute(Task task) { try { - processOutputDirectory(compileTask.getDestinationDir(), compileTask.getClasspath()); + processOutputDirectory(this.task.getDestinationDir(), this.task.getClasspath()); } catch (IOException exception) { throw new GradleException("Error accessing file system", exception); } } + /** + * Processes all class files within the given directory. + * + * @param root The root directory to process. + * @param classPath A list of class path elements expected by the processed classes. + * @throws IOException If an I/O exception occurs. + */ private void processOutputDirectory(File root, Iterable classPath) throws IOException { if (!root.isDirectory()) { throw new GradleException("Target location does not exist or is no directory: " + root); @@ -72,6 +101,15 @@ private void processOutputDirectory(File root, Iterable classPat } } + /** + * Applies all registered transformations. + * + * @param root The root directory to process. + * @param entryPoint The transformation's entry point. + * @param classPath A list of class path elements expected by the processed classes. + * @param plugins The plugins to apply. + * @throws IOException If an I/O exception occurs. + */ private void transform(File root, Iterable classPath, EntryPoint entryPoint, List plugins) throws IOException { List classFileLocators = new ArrayList(); classFileLocators.add(new ClassFileLocator.ForFolder(root)); @@ -97,9 +135,7 @@ private void transform(File root, Iterable classPath, EntryPoint root, byteBuddy, entryPoint, - byteBuddyExtension.getSuffix() == null || byteBuddyExtension.getSuffix().isEmpty() - ? MethodNameTransformer.Suffixing.withRandomSuffix() - : new MethodNameTransformer.Suffixing(byteBuddyExtension.getSuffix()), + byteBuddyExtension.getMethodNameTransformer(), classFileLocator, typePool, plugins); @@ -108,6 +144,18 @@ private void transform(File root, Iterable classPath, EntryPoint } } + /** + * Processes a directory. + * + * @param root The root directory to process. + * @param folder The currently processed folder. + * @param byteBuddy The Byte Buddy instance to use. + * @param entryPoint The transformation's entry point. + * @param methodNameTransformer The method name transformer to use. + * @param classFileLocator The class file locator to use. + * @param typePool The type pool to query for type descriptions. + * @param plugins The plugins to apply. + */ private void processDirectory(File root, File folder, ByteBuddy byteBuddy, @@ -137,6 +185,18 @@ private void processDirectory(File root, } } + /** + * Processes a class file. + * + * @param root The root directory to process. + * @param file The class file to process. + * @param byteBuddy The Byte Buddy instance to use. + * @param entryPoint The transformation's entry point. + * @param methodNameTransformer The method name transformer to use. + * @param classFileLocator The class file locator to use. + * @param typePool The type pool to query for type descriptions. + * @param plugins The plugins to apply. + */ private void processClassFile(File root, String file, ByteBuddy byteBuddy, diff --git a/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/package-info.java b/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/package-info.java new file mode 100644 index 00000000000..19b2760dc4c --- /dev/null +++ b/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/package-info.java @@ -0,0 +1,4 @@ +/** + * A package containing classes for applying Byte Buddy transformers within a Gradle build. + */ +package net.bytebuddy.build.gradle; diff --git a/byte-buddy-maven-plugin/src/main/java/net/bytebuddy/build/maven/Transformation.java b/byte-buddy-maven-plugin/src/main/java/net/bytebuddy/build/maven/Transformation.java index 126b26e98a2..6ef0527fcac 100644 --- a/byte-buddy-maven-plugin/src/main/java/net/bytebuddy/build/maven/Transformation.java +++ b/byte-buddy-maven-plugin/src/main/java/net/bytebuddy/build/maven/Transformation.java @@ -28,7 +28,7 @@ public String getPlugin() throws MojoExecutionException { } /** - * Returns the plugin name or null if not set. + * Returns the plugin name or {@code null} if it is not set. * * @return The configured plugin name. */