• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

raphw / byte-buddy / #801

27 Oct 2025 09:37AM UTC coverage: 84.715% (-0.4%) from 85.118%
#801

push

raphw
Fix imports.

29586 of 34924 relevant lines covered (84.72%)

0.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

37.05
/byte-buddy-maven-plugin/src/main/java/net/bytebuddy/build/maven/ByteBuddyMojo.java
1
/*
2
 * Copyright 2014 - Present Rafael Winterhalter
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package net.bytebuddy.build.maven;
17

18
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19
import net.bytebuddy.ClassFileVersion;
20
import net.bytebuddy.build.BuildLogger;
21
import net.bytebuddy.build.EntryPoint;
22
import net.bytebuddy.build.Plugin;
23
import net.bytebuddy.description.type.TypeDescription;
24
import net.bytebuddy.dynamic.ClassFileLocator;
25
import net.bytebuddy.dynamic.scaffold.inline.MethodNameTransformer;
26
import net.bytebuddy.matcher.ElementMatcher;
27
import net.bytebuddy.utility.CompoundList;
28
import net.bytebuddy.utility.nullability.MaybeNull;
29
import net.bytebuddy.utility.nullability.UnknownNull;
30
import org.apache.maven.artifact.Artifact;
31
import org.apache.maven.artifact.DependencyResolutionRequiredException;
32
import org.apache.maven.model.Dependency;
33
import org.apache.maven.model.PluginManagement;
34
import org.apache.maven.plugin.AbstractMojo;
35
import org.apache.maven.plugin.MojoExecution;
36
import org.apache.maven.plugin.MojoExecutionException;
37
import org.apache.maven.plugin.MojoFailureException;
38
import org.apache.maven.plugin.logging.Log;
39
import org.apache.maven.plugins.annotations.Component;
40
import org.apache.maven.plugins.annotations.LifecyclePhase;
41
import org.apache.maven.plugins.annotations.Mojo;
42
import org.apache.maven.plugins.annotations.Parameter;
43
import org.apache.maven.plugins.annotations.ResolutionScope;
44
import org.apache.maven.project.MavenProject;
45
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
46
import org.codehaus.plexus.util.Scanner;
47
import org.codehaus.plexus.util.xml.Xpp3Dom;
48
import org.eclipse.aether.RepositorySystem;
49
import org.eclipse.aether.RepositorySystemSession;
50
import org.eclipse.aether.collection.CollectRequest;
51
import org.eclipse.aether.collection.DependencyCollectionException;
52
import org.eclipse.aether.graph.DependencyNode;
53
import org.eclipse.aether.resolution.DependencyRequest;
54
import org.eclipse.aether.resolution.DependencyResolutionException;
55
import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator;
56
import org.sonatype.plexus.build.incremental.BuildContext;
57

58
import java.io.Closeable;
59
import java.io.File;
60
import java.io.IOException;
61
import java.net.MalformedURLException;
62
import java.net.URL;
63
import java.net.URLClassLoader;
64
import java.util.ArrayList;
65
import java.util.Collections;
66
import java.util.HashMap;
67
import java.util.HashSet;
68
import java.util.List;
69
import java.util.Map;
70
import java.util.Set;
71

72
/**
73
 * A Maven plugin for applying Byte Buddy transformations during a build.
74
 */
75
public abstract class ByteBuddyMojo extends AbstractMojo {
1✔
76

77
    /**
78
     * The file extension for Java source files.
79
     */
80
    private static final String JAVA_FILE_EXTENSION = ".java";
81

82
    /**
83
     * The file extension for Java class files.
84
     */
85
    private static final String JAVA_CLASS_EXTENSION = ".class";
86

87
    /**
88
     * The Maven project.
89
     */
90
    @UnknownNull
91
    @Parameter(defaultValue = "${project}", readonly = true)
92
    public MavenProject project;
93

94
    /**
95
     * The current execution of this plugin.
96
     */
97
    @UnknownNull
98
    @Parameter(defaultValue = "${mojoExecution}", readonly = true)
99
    public MojoExecution execution;
100

101
    /**
102
     * The currently used repository system.
103
     */
104
    @UnknownNull
105
    @Component
106
    public RepositorySystem repositorySystem;
107

108
    /**
109
     * The currently used system session for the repository system.
110
     */
111
    @MaybeNull
112
    @Parameter(defaultValue = "${repositorySystemSession}", readonly = true)
113
    public RepositorySystemSession repositorySystemSession;
114

115
    /**
116
     * <p>
117
     * The list of transformations. A transformation <b>must</b> specify the {@code plugin} property, containing the name of a class to apply.
118
     * Additionally, it is possible to optionally specify Maven coordinates for a project that contains this plugin class as {@code groupId},
119
     * {@code artifactId} and {@code version}. If any of the latter properties is not set, this projects coordinate is used.
120
     * </p>
121
     * <p>
122
     * For example, the following configuration applies the {@code foo.Bar} class which must implement {@link Plugin} from artifact
123
     * {@code transform-artifact} with this project's group and version:
124
     * </p>
125
     * <blockquote><pre>{@code
126
     * <transformations>
127
     *   <transformation>
128
     *     <plugin>foo.Bar< /plugin>
129
     *     <artifactId>transform-artifact< /artifactId>
130
     *   < /transformation>
131
     * < /transformations>
132
     * }</pre></blockquote>
133
     * <p>
134
     * If the list of {@code transformations} is empty or is not supplied at all, this plugin does not apply but prints a warning.
135
     * </p>
136
     */
137
    @MaybeNull
138
    @Parameter
139
    public List<Transformation> transformations;
140

141
    /**
142
     * <p>
143
     * The initializer used for creating a {@link net.bytebuddy.ByteBuddy} instance and for applying a transformation. By default,
144
     * a type is rebased. The initializer's {@code entryPoint} property can be set to any constant name of {@link EntryPoint.Default}
145
     * or to a class name. If the latter applies, it is possible to set Maven coordinates for a Maven plugin which defines this
146
     * class where any property defaults to this project's coordinates.
147
     * </p>
148
     * <p>
149
     * For example, the following configuration applies the {@code foo.Qux} class which must implement {@link EntryPoint} from
150
     * artifact {@code initialization-artifact} with this project's group and version:
151
     * </p>
152
     * <blockquote><pre>{@code
153
     * <initialization>
154
     *   <entryPoint>foo.Qux< /entryPoint>
155
     *   <artifactId>initialization-artifact< /artifactId>
156
     * < /initialization>
157
     * }</pre></blockquote>
158
     */
159
    @MaybeNull
160
    @Parameter
161
    public Initialization initialization;
162

163
    /**
164
     * Specifies the method name suffix that is used when type's method need to be rebased. If this property is not
165
     * set or is empty, a random suffix will be appended to any rebased method. If this property is set, the supplied
166
     * value is appended to the original method name.
167
     */
168
    @MaybeNull
169
    @Parameter
170
    public String suffix;
171

172
    /**
173
     * When transforming classes during build time, it is not possible to apply any transformations which require a class
174
     * in its loaded state. Such transformations might imply setting a type's static field to a user interceptor or similar
175
     * transformations. If this property is set to {@code false}, this plugin does not throw an exception if such a live
176
     * initializer is defined during a transformation process.
177
     */
178
    @Parameter(defaultValue = "true", required = true)
179
    public boolean failOnLiveInitializer;
180

181
    /**
182
     * When set to {@code true}, this mojo is not applied to the current module.
183
     */
184
    @Parameter(defaultValue = "false", required = true)
185
    public boolean skip;
186

187
    /**
188
     * When set to {@code true}, this mojo warns of an non-existent output directory.
189
     */
190
    @Parameter(defaultValue = "true", required = true)
191
    public boolean warnOnMissingOutputDirectory;
192

193
    /**
194
     * When set to {@code true}, this mojo warns of not having transformed any types.
195
     */
196
    @Parameter(defaultValue = "true", required = true)
197
    public boolean warnOnEmptyTypeSet;
198

199
    /**
200
     * When set to {@code true}, this mojo fails immediately if a plugin cannot be applied.
201
     */
202
    @Parameter(defaultValue = "true", required = true)
203
    public boolean failFast;
204

205
    /**
206
     * When set to {@code true}, the debug information of class files should be parsed to extract parameter names.
207
     */
208
    @Parameter(defaultValue = "false", required = true)
209
    public boolean extendedParsing;
210

211
    /**
212
     * Determines if the build should discover Byte Buddy build plugins on this Maven plugin's class loader.
213
     * Discovered plugins are stored by their name in the <i>/META-INF/net.bytebuddy/build.plugins</i> file
214
     * where each line contains the fully qualified class name. Discovered plugins are not provided with any
215
     * explicit constructor arguments.
216
     */
217
    @MaybeNull
218
    @Parameter(defaultValue = "EMPTY", required = true)
219
    public Discovery discovery;
220

221
    /**
222
     * Scans the class path (or test class path) for Byte Buddy plugins to apply. This is not normally recommended as
223
     * it might cause a spurious application of plugins that are accidentally configured on the class path. It can
224
     * however serve as a convenience in projects with few dependencies where this allows for the use of Maven's
225
     * dependency version management.
226
     */
227
    @Parameter(defaultValue = "false", required = true)
228
    public boolean classPathDiscovery;
229

230
    /**
231
     * Indicates the amount of threads used for parallel type processing or {@code 0} for serial processing.
232
     */
233
    @Parameter(defaultValue = "0", required = true)
234
    public int threads;
235

236
    /**
237
     * Determines the tolerance of many milliseconds between this plugin run and the last edit are permitted
238
     * for considering a file as stale if the plugin was executed before. Can be set to {@code -1} to disable.
239
     */
240
    @Parameter(defaultValue = "0", required = true)
241
    public int staleMilliseconds;
242

243
    /**
244
     * Defines the version to use for resolving multi-release jar files. If not set, the Java compile version is used.
245
     */
246
    @MaybeNull
247
    @Parameter
248
    public Integer multiReleaseVersion;
249

250
    /**
251
     * {@inheritDoc}
252
     */
253
    @SuppressFBWarnings(value = "DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED", justification = "The security manager is not normally used within Maven.")
254
    public void execute() throws MojoExecutionException, MojoFailureException {
255
        if (project == null || repositorySystem == null || discovery == null) {
1✔
256
            throw new MojoExecutionException("Plugin is not initialized correctly");
×
257
        } else if (skip) {
1✔
258
            getLog().info("Not applying instrumentation as a result of plugin configuration.");
×
259
            return;
×
260
        }
261
        List<Transformer> transformers = new ArrayList<Transformer>();
1✔
262
        Set<String> undiscoverable = new HashSet<String>();
1✔
263
        if (transformations != null) {
1✔
264
            for (Transformation transformation : transformations) {
1✔
265
                transformers.add(new Transformer.ForConfiguredPlugin(transformation));
1✔
266
                if (discovery.isRecordConfiguration()) {
1✔
267
                    undiscoverable.add(transformation.getPlugin());
1✔
268
                }
269
            }
1✔
270
        }
271
        Map<Coordinate, String> coordinates = new HashMap<Coordinate, String>();
1✔
272
        if (project.getDependencyManagement() != null) {
1✔
273
            for (Dependency dependency : project.getDependencyManagement().getDependencies()) {
×
274
                coordinates.put(new Coordinate(dependency.getGroupId(), dependency.getArtifactId()), dependency.getVersion());
×
275
            }
×
276
        }
277
        List<String> elements = resolveClassPathElements(coordinates);
1✔
278
        if (discovery.isDiscover(transformers)) {
1✔
279
            try {
280
                for (String name : Plugin.Engine.Default.scan(ByteBuddyMojo.class.getClassLoader())) {
1✔
281
                    if (undiscoverable.add(name)) {
×
282
                        transformers.add(new Transformer.ForDiscoveredPlugin(name));
×
283
                        getLog().debug("Registered discovered plugin: " + name);
×
284
                    } else {
285
                        getLog().info("Skipping discovered plugin " + name + " which was previously discovered or registered");
×
286
                    }
287
                }
×
288
                if (classPathDiscovery) {
1✔
289
                    List<URL> urls = new ArrayList<URL>(elements.size());
×
290
                    for (String element : elements) {
×
291
                        urls.add(new File(element).toURI().toURL());
×
292
                    }
×
293
                    ClassLoader classLoader = new URLClassLoader(urls.toArray(new URL[0]));
×
294
                    try {
295
                        for (String name : Plugin.Engine.Default.scan(classLoader)) {
×
296
                            if (undiscoverable.add(name)) {
×
297
                                transformers.add(new Transformer.ForDiscoveredPlugin.FromClassLoader(name, elements));
×
298
                                getLog().debug("Registered discovered plugin: " + name);
×
299
                            } else {
300
                                getLog().info("Skipping discovered plugin " + name + " which was previously discovered or registered");
×
301
                            }
302
                        }
×
303
                    } finally {
304
                        if (classLoader instanceof Closeable) {
×
305
                            ((Closeable) classLoader).close();
×
306
                        }
307
                    }
308
                }
309
            } catch (IOException exception) {
×
310
                throw new MojoExecutionException("Failed plugin discovery", exception);
×
311
            }
1✔
312
        }
313
        if (transformers.isEmpty()) {
1✔
314
            getLog().warn("No transformations are specified or discovered. Skipping plugin application.");
1✔
315
            return;
1✔
316
        } else {
317
            getLog().debug(transformers.size() + " plugins are being applied via configuration and discovery");
1✔
318
        }
319
        try {
320
            apply(transformers, elements, coordinates);
1✔
321
        } catch (IOException exception) {
×
322
            throw new MojoFailureException("Error during writing process", exception);
×
323
        }
1✔
324
    }
1✔
325

326
    /**
327
     * Resolves the class path elements of the relevant output directory.
328
     *
329
     * @param coordinates Versions for managed dependencies.
330
     * @return The class path elements of the relevant output directory.
331
     * @throws MojoExecutionException If the user configuration results in an error.
332
     * @throws MojoFailureException   If the plugin application raises an error.
333
     */
334
    protected abstract List<String> resolveClassPathElements(Map<Coordinate, String> coordinates) throws MojoExecutionException, MojoFailureException;
335

336
    /**
337
     * Applies this mojo for the given setup.
338
     *
339
     * @param transformers The transformers to apply.
340
     * @param elements     The class path elements to consider.
341
     * @param coordinates  Versions for managed dependencies.
342
     * @throws MojoExecutionException If the plugin fails due to a user error.
343
     * @throws MojoFailureException   If the plugin fails due to an application error.
344
     * @throws IOException            If an I/O exception occurs.
345
     */
346
    protected abstract void apply(List<Transformer> transformers, List<String> elements, Map<Coordinate, String> coordinates) throws MojoExecutionException, MojoFailureException, IOException;
347

348
    /**
349
     * Applies the instrumentation.
350
     *
351
     * @param classPath    An iterable over all class path elements.
352
     * @param coordinates  Versions for managed dependencies.
353
     * @param transformers The transformers to apply.
354
     * @param source       The source for the plugin engine's application.
355
     * @param target       The target for the plugin engine's application.
356
     * @param file         The file representing the source location.
357
     * @param filtered     {@code true} if files are already filtered and should not be checked for staleness.
358
     * @return A summary of the applied transformation.
359
     * @throws MojoExecutionException If the plugin cannot be applied.
360
     * @throws IOException            If an I/O exception occurs.
361
     */
362
    @SuppressWarnings("unchecked")
363
    protected Plugin.Engine.Summary transform(List<? extends String> classPath,
364
                                              Map<Coordinate, String> coordinates,
365
                                              List<Transformer> transformers,
366
                                              Plugin.Engine.Source source,
367
                                              Plugin.Engine.Target target,
368
                                              File file,
369
                                              boolean filtered) throws MojoExecutionException, IOException {
370
        File staleness = new File(project.getBuild().getDirectory(), "maven-status"
1✔
371
                + File.separator + execution.getArtifactId()
1✔
372
                + File.separator + execution.getGoal()
1✔
373
                + File.separator + execution.getExecutionId()
1✔
374
                + File.separator + "staleness");
375
        StalenessFilter stalenessFilter;
376
        if (filtered || staleMilliseconds < 0) {
1✔
377
            stalenessFilter = null;
1✔
378
            getLog().debug("Stale file detection is disabled");
1✔
379
        } else if (staleness.exists()) {
×
380
            stalenessFilter = new StalenessFilter(getLog(), staleness.lastModified() + staleMilliseconds);
×
381
            source = new Plugin.Engine.Source.Filtering(source, stalenessFilter);
×
382
            getLog().debug("Using stale file detection with a margin of " + staleMilliseconds + " milliseconds");
×
383
        } else {
384
            stalenessFilter = null;
×
385
            getLog().debug("Did not discover previous staleness file");
×
386
        }
387
        List<File> artifacts = new ArrayList<File>(classPath.size());
1✔
388
        for (String element : classPath) {
1✔
389
            artifacts.add(new File(element));
1✔
390
        }
1✔
391
        ClassLoaderResolver classLoaderResolver = new ClassLoaderResolver(getLog(), repositorySystem, repositorySystemSession == null ? MavenRepositorySystemUtils.newSession() : repositorySystemSession, project.getRemotePluginRepositories());
1✔
392
        try {
393
            List<Plugin.Factory> factories = new ArrayList<Plugin.Factory>(transformers.size());
1✔
394
            for (Transformer transformer : transformers) {
1✔
395
                String plugin = transformer.getPlugin();
1✔
396
                try {
397
                    factories.add(new Plugin.Factory.UsingReflection((Class<? extends Plugin>) Class.forName(plugin, false, transformer.toClassLoader(classLoaderResolver, coordinates, project.getGroupId(), project.getArtifactId(), project.getVersion(), project.getPackaging())))
1✔
398
                            .with(transformer.toArgumentResolvers())
1✔
399
                            .with(Plugin.Factory.UsingReflection.ArgumentResolver.ForType.of(File.class, file),
1✔
400
                                    Plugin.Factory.UsingReflection.ArgumentResolver.ForType.of(Log.class, getLog()),
1✔
401
                                    Plugin.Factory.UsingReflection.ArgumentResolver.ForType.of(BuildLogger.class, new MavenBuildLogger(getLog())),
1✔
402
                                    Plugin.Factory.UsingReflection.ArgumentResolver.ForType.of(File[].class, artifacts.toArray(new File[0]))));
1✔
403
                    getLog().info("Resolved plugin: " + plugin);
1✔
404
                } catch (Throwable throwable) {
×
405
                    throw new MojoExecutionException("Cannot resolve plugin: " + plugin, throwable);
×
406
                }
1✔
407
            }
1✔
408
            String managed = coordinates.get(new Coordinate(project.getGroupId(), project.getArtifactId()));
1✔
409
            EntryPoint entryPoint = (initialization == null ? new Initialization() : initialization).getEntryPoint(classLoaderResolver, project.getGroupId(), project.getArtifactId(), managed == null ? project.getVersion() : managed, project.getPackaging());
1✔
410
            getLog().info("Resolved entry point: " + entryPoint);
1✔
411
            String javaVersionString = findJavaVersionString(project, "release");
1✔
412
            if (javaVersionString == null) {
1✔
413
                javaVersionString = findJavaVersionString(project, "target");
1✔
414
            }
415
            ClassFileVersion classFileVersion;
416
            if (javaVersionString == null) {
1✔
417
                classFileVersion = ClassFileVersion.ofThisVm(ClassFileVersion.JAVA_V5);
×
418
                getLog().warn("Could not locate Java target version, build is JDK dependant: " + classFileVersion.getMajorVersion());
×
419
            } else {
420
                classFileVersion = ClassFileVersion.ofJavaVersionString(javaVersionString);
1✔
421
                getLog().debug("Java version detected: " + javaVersionString);
1✔
422
            }
423
            ClassFileVersion multiReleaseClassFileVersion = multiReleaseVersion == null
1✔
424
                    ? classFileVersion
425
                    : ClassFileVersion.ofJavaVersion(multiReleaseVersion);
1✔
426
            List<ClassFileLocator> classFileLocators = new ArrayList<ClassFileLocator>(artifacts.size());
1✔
427
            classFileLocators.add(ClassFileLocator.ForClassLoader.ofPlatformLoader());
1✔
428
            for (File artifact : artifacts) {
1✔
429
                classFileLocators.add(artifact.isFile()
1✔
430
                        ? ClassFileLocator.ForJarFile.of(artifact, multiReleaseClassFileVersion)
1✔
431
                        : ClassFileLocator.ForFolder.of(artifact, multiReleaseClassFileVersion));
1✔
432
            }
1✔
433
            ClassFileLocator classFileLocator = new ClassFileLocator.Compound(classFileLocators);
1✔
434
            Plugin.Engine.Summary summary;
435
            try {
436
                getLog().info("Processing class files located in in: " + file);
1✔
437
                Plugin.Engine pluginEngine;
438
                try {
439
                    pluginEngine = Plugin.Engine.Default.of(entryPoint,
1✔
440
                            classFileVersion,
441
                            suffix == null || suffix.length() == 0 ? MethodNameTransformer.Suffixing.withRandomSuffix() : new MethodNameTransformer.Suffixing(suffix));
1✔
442
                } catch (Throwable throwable) {
1✔
443
                    throw new MojoExecutionException("Cannot create plugin engine", throwable);
1✔
444
                }
1✔
445
                try {
446
                    summary = pluginEngine
1✔
447
                            .with(extendedParsing ? Plugin.Engine.PoolStrategy.Default.EXTENDED : Plugin.Engine.PoolStrategy.Default.FAST)
1✔
448
                            .with(classFileLocator)
1✔
449
                            .with(multiReleaseClassFileVersion)
1✔
450
                            .with(new TransformationLogger(getLog()))
1✔
451
                            .withErrorHandlers(Plugin.Engine.ErrorHandler.Enforcing.ALL_TYPES_RESOLVED,
1✔
452
                                    failOnLiveInitializer ? Plugin.Engine.ErrorHandler.Enforcing.NO_LIVE_INITIALIZERS : Plugin.Engine.Listener.NoOp.INSTANCE,
453
                                    failFast ? Plugin.Engine.ErrorHandler.Failing.FAIL_FAST : Plugin.Engine.ErrorHandler.Failing.FAIL_LAST)
454
                            .with(threads == 0 ? Plugin.Engine.Dispatcher.ForSerialTransformation.Factory.INSTANCE : new Plugin.Engine.Dispatcher.ForParallelTransformation.WithThrowawayExecutorService.Factory(threads))
1✔
455
                            .apply(source, target, factories);
1✔
456
                } catch (Throwable throwable) {
1✔
457
                    throw new MojoExecutionException("Failed to transform class files in " + file, throwable);
1✔
458
                }
1✔
459
            } finally {
460
                classFileLocator.close();
1✔
461
            }
462
            if (!summary.getFailed().isEmpty()) {
1✔
463
                throw new MojoExecutionException(summary.getFailed() + " type transformation(s) have failed");
×
464
            } else if (warnOnEmptyTypeSet && summary.getTransformed().isEmpty()) {
1✔
465
                if (stalenessFilter != null && stalenessFilter.getFiltered() > 0) {
×
466
                    getLog().info("No types were transformed during plugin execution but " + stalenessFilter.getFiltered() + " class file(s) were considered stale");
×
467
                } else {
468
                    getLog().warn("No types were transformed during plugin execution");
×
469
                }
470
            } else {
471
                getLog().info("Transformed " + summary.getTransformed().size() + " type(s)");
1✔
472
            }
473
            if (!(staleness.getParentFile().isDirectory() || staleness.getParentFile().mkdirs()) || (!staleness.createNewFile() && (!staleness.delete() || !staleness.createNewFile()))) {
1✔
474
                throw new MojoExecutionException("Failed to define instrumentation staleness: " + staleness.getAbsolutePath());
×
475
            }
476
            return summary;
1✔
477
        } finally {
478
            classLoaderResolver.close();
1✔
479
        }
480
    }
481

482
    /**
483
     * Makes a best effort of locating the configured Java version.
484
     *
485
     * @param project  The relevant Maven project.
486
     * @param property The targeted Maven property.
487
     * @return The Java version string of the configured build Java version or {@code null} if no explicit configuration was detected.
488
     */
489
    @MaybeNull
490
    private static String findJavaVersionString(MavenProject project, String property) {
491
        do {
492
            String value = project.getProperties().getProperty("maven.compiler." + property);
1✔
493
            if (value != null) {
1✔
494
                return value;
1✔
495
            }
496
            PluginManagement management = project.getPluginManagement();
1✔
497
            for (org.apache.maven.model.Plugin plugin : management == null ? project.getBuildPlugins() : CompoundList.of(project.getBuildPlugins(), management.getPlugins())) {
1✔
498
                if ("maven-compiler-plugin".equals(plugin.getArtifactId())) {
×
499
                    if (plugin.getConfiguration() instanceof Xpp3Dom) {
×
500
                        Xpp3Dom node = ((Xpp3Dom) plugin.getConfiguration()).getChild(property);
×
501
                        if (node != null) {
×
502
                            return node.getValue();
×
503
                        }
504
                    }
505
                }
506
            }
×
507
            project = project.getParent();
1✔
508
        } while (project != null);
1✔
509
        return null;
1✔
510
    }
511

512
    /**
513
     * Matches elements which represent a Java class that is represented in the list or an inner class of the classes represented in the list.
514
     */
515
    private static class FilePrefixMatcher extends ElementMatcher.Junction.ForNonNullValues<Plugin.Engine.Source.Element> {
516

517
        /**
518
         * A list of names to match.
519
         */
520
        private final List<String> names;
521

522
        /**
523
         * Create a new matcher for a list of names.
524
         *
525
         * @param names A list of included names.
526
         */
527
        private FilePrefixMatcher(List<String> names) {
×
528
            this.names = names;
×
529
        }
×
530

531
        /**
532
         * {@inheritDoc}
533
         */
534
        protected boolean doMatch(Plugin.Engine.Source.Element target) {
535
            for (String name : names) {
×
536
                if (target.getName().equals(name + JAVA_CLASS_EXTENSION) || target.getName().startsWith(name + "$") && target.getName().endsWith(JAVA_CLASS_EXTENSION)) {
×
537
                    return true;
×
538
                }
539
            }
×
540
            return false;
×
541
        }
542
    }
543

544
    /**
545
     * A version of the plugin that is bound to Maven's lifecycle.
546
     */
547
    public abstract static class ForLifecycleTypes extends ByteBuddyMojo {
1✔
548

549
        /**
550
         * The build context to support incremental builds.
551
         */
552
        @MaybeNull
553
        @Component
554
        public BuildContext context;
555

556
        /**
557
         * Determines if plugins are attempted to be built incrementally.
558
         */
559
        @Parameter(defaultValue = "false", required = true)
560
        public boolean incremental;
561

562
        /**
563
         * Returns the output directory to search for class files.
564
         *
565
         * @return The output directory to search for class files.
566
         */
567
        protected abstract String getOutputDirectory();
568

569
        /**
570
         * Returns the source directory that determines the class files to process.
571
         *
572
         * @return The source directory that serves as an input for the transformation.
573
         */
574
        @MaybeNull
575
        protected abstract String getSourceDirectory();
576

577
        @Override
578
        protected void apply(List<Transformer> transformers, List<String> elements, Map<Coordinate, String> coordinates) throws MojoExecutionException, IOException {
579
            File root = new File(getOutputDirectory());
1✔
580
            if (!root.exists()) {
1✔
581
                if (warnOnMissingOutputDirectory) {
×
582
                    getLog().warn("Skipping instrumentation due to missing directory: " + root);
×
583
                } else {
584
                    getLog().info("Skipping instrumentation due to missing directory: " + root);
×
585
                }
586
                return;
×
587
            } else if (!root.isDirectory()) {
1✔
588
                throw new MojoExecutionException("Not a directory: " + root);
×
589
            }
590
            String sourceDirectory = getSourceDirectory();
1✔
591
            if (incremental && context != null && sourceDirectory != null) {
1✔
592
                getLog().debug("Considering incremental build with context: " + context);
×
593
                Plugin.Engine.Source source;
594
                if (context.isIncremental()) {
×
595
                    Scanner scanner = context.newScanner(new File(sourceDirectory));
×
596
                    scanner.scan();
×
597
                    List<String> names = new ArrayList<String>();
×
598
                    for (String file : scanner.getIncludedFiles()) {
×
599
                        if (file.endsWith(JAVA_FILE_EXTENSION)) {
×
600
                            names.add(file.substring(0, file.length() - JAVA_FILE_EXTENSION.length()));
×
601
                        }
602
                    }
603
                    source = new Plugin.Engine.Source.Filtering(new Plugin.Engine.Source.ForFolder(root), new FilePrefixMatcher(names));
×
604
                    getLog().debug("Incrementally processing: " + names);
×
605
                } else {
×
606
                    source = new Plugin.Engine.Source.ForFolder(root);
×
607
                    getLog().debug("Cannot build incrementally - all class files are processed");
×
608
                }
609
                Plugin.Engine.Summary summary = transform(elements, coordinates, transformers, source, new Plugin.Engine.Target.ForFolder(root), root, true);
×
610
                for (TypeDescription typeDescription : summary.getTransformed()) {
×
611
                    context.refresh(new File(getOutputDirectory(), typeDescription.getName() + JAVA_CLASS_EXTENSION));
×
612
                }
×
613
            } else {
×
614
                getLog().debug("Not applying incremental build with context: " + context);
1✔
615
                transform(elements, coordinates, transformers, new Plugin.Engine.Source.ForFolder(root), new Plugin.Engine.Target.ForFolder(root), root, false);
1✔
616
            }
617
        }
1✔
618

619
        /**
620
         * A Byte Buddy plugin that transforms a project's production class files.
621
         */
622
        public abstract static class ForProductionTypes extends ForLifecycleTypes {
1✔
623

624
            @Override
625
            protected String getOutputDirectory() {
626
                return project.getBuild().getOutputDirectory();
1✔
627
            }
628

629
            @MaybeNull
630
            @Override
631
            protected String getSourceDirectory() {
632
                return project.getBuild().getSourceDirectory();
1✔
633
            }
634

635
            /**
636
             * A Byte Buddy plugin that transforms a project's production class files where runtime class
637
             * path elements are not included.
638
             */
639
            @Mojo(name = "transform", defaultPhase = LifecyclePhase.PROCESS_CLASSES, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE)
640
            public static class WithoutRuntimeDependencies extends ForProductionTypes {
1✔
641

642
                @Override
643
                protected List<String> resolveClassPathElements(Map<Coordinate, String> coordinates) throws MojoFailureException {
644
                    try {
645
                        return project.getCompileClasspathElements();
1✔
646
                    } catch (DependencyResolutionRequiredException exception) {
×
647
                        throw new MojoFailureException("Could not resolve class path", exception);
×
648
                    }
649
                }
650
            }
651

652
            /**
653
             * A Byte Buddy plugin that transforms a project's production class files where runtime class
654
             * path elements are included.
655
             */
656
            @Mojo(name = "transform-runtime", defaultPhase = LifecyclePhase.PROCESS_CLASSES, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
657
            public static class WithRuntimeDependencies extends ForProductionTypes {
×
658

659
                @Override
660
                protected List<String> resolveClassPathElements(Map<Coordinate, String> coordinates) {
661
                    try {
662
                        return project.getRuntimeClasspathElements();
×
663
                    } catch (DependencyResolutionRequiredException exception) {
×
664
                        throw new RuntimeException("Could not resolve runtime class path", exception);
×
665
                    }
666
                }
667
            }
668

669
            /**
670
             * A Byte Buddy plugin that transforms a project's production class files where all scopes but the test scope are included.
671
             */
672
            @Mojo(name = "transform-extended", defaultPhase = LifecyclePhase.PROCESS_CLASSES, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
673
            public static class WithExtendedDependencies extends ForProductionTypes {
×
674

675
                @Override
676
                protected List<String> resolveClassPathElements(Map<Coordinate, String> coordinates) {
677
                    List<String> classPath = new ArrayList<String>(project.getArtifacts().size() + 1);
×
678
                    String directory = project.getBuild().getOutputDirectory();
×
679
                    if (directory != null) {
×
680
                        classPath.add(directory);
×
681
                    }
682
                    for (Artifact artifact : project.getArtifacts()) {
×
683
                        if (artifact.getArtifactHandler().isAddedToClasspath()
×
684
                                && !Artifact.SCOPE_TEST.equals(artifact.getScope())
×
685
                                && !Artifact.SCOPE_IMPORT.equals(artifact.getScope())) {
×
686
                            File file = artifact.getFile();
×
687
                            if (file != null) {
×
688
                                classPath.add(file.getPath());
×
689
                            }
690
                        }
691
                    }
×
692
                    return classPath;
×
693
                }
694
            }
695
        }
696

697
        /**
698
         * A Byte Buddy plugin that transforms a project's test class files.
699
         */
700
        @Mojo(name = "transform-test", defaultPhase = LifecyclePhase.PROCESS_TEST_CLASSES, threadSafe = true, requiresDependencyResolution = ResolutionScope.TEST)
701
        public static class ForTestTypes extends ForLifecycleTypes {
×
702

703
            @Override
704
            protected String getOutputDirectory() {
705
                return project.getBuild().getTestOutputDirectory();
×
706
            }
707

708
            @MaybeNull
709
            @Override
710
            protected String getSourceDirectory() {
711
                return project.getBuild().getTestSourceDirectory();
×
712
            }
713

714
            @Override
715
            protected List<String> resolveClassPathElements(Map<Coordinate, String> coordinates) throws MojoFailureException {
716
                try {
717
                    return project.getTestClasspathElements();
×
718
                } catch (DependencyResolutionRequiredException exception) {
×
719
                    throw new MojoFailureException("Could not resolve test class path", exception);
×
720
                }
721
            }
722
        }
723
    }
724

725
    /**
726
     * Transforms specified classes from files in a folder or a jar file to a folder or jar file.
727
     */
728
    @Mojo(name = "transform-location-empty", defaultPhase = LifecyclePhase.PROCESS_CLASSES, threadSafe = true)
729
    public static class ForExplicitLocations extends ByteBuddyMojo {
×
730

731
        /**
732
         * The source folder or jar file that should be transformed.
733
         */
734
        @UnknownNull
735
        @Parameter(required = true)
736
        public String source;
737

738
        /**
739
         * The target folder or jar file to where the transformed sources should be written to.
740
         */
741
        @UnknownNull
742
        @Parameter(required = true)
743
        public String target;
744

745
        /**
746
         * A list of dependencies to be included when resolving class files, additionally to the source jar.
747
         */
748
        @MaybeNull
749
        @Parameter
750
        public List<CoordinateConfiguration> dependencies;
751

752
        @Override
753
        protected List<String> resolveClassPathElements(Map<Coordinate, String> coordinates) throws MojoExecutionException, MojoFailureException {
754
            List<String> classPath = new ArrayList<String>();
×
755
            classPath.add(source);
×
756
            classPath.addAll(resolveImplicitClassPathElements());
×
757
            if (dependencies != null && !dependencies.isEmpty()) {
×
758
                RepositorySystemSession repositorySystemSession = this.repositorySystemSession == null ? MavenRepositorySystemUtils.newSession() : this.repositorySystemSession;
×
759
                for (CoordinateConfiguration dependency : dependencies) {
×
760
                    String managed = coordinates.get(new Coordinate(dependency.getGroupId(project.getGroupId()), dependency.getArtifactId(project.getArtifactId())));
×
761
                    MavenCoordinate mavenCoordinate = dependency.asCoordinate(project.getGroupId(),
×
762
                            project.getArtifactId(),
×
763
                            managed == null ? project.getVersion() : managed,
×
764
                            project.getPackaging());
×
765
                    try {
766
                        DependencyNode root = repositorySystem.collectDependencies(
×
767
                                repositorySystemSession,
768
                                new CollectRequest(new org.eclipse.aether.graph.Dependency(mavenCoordinate.asArtifact(), "runtime"), project.getRemotePluginRepositories())).getRoot();
×
769
                        repositorySystem.resolveDependencies(repositorySystemSession, new DependencyRequest().setRoot(root));
×
770
                        PreorderNodeListGenerator preorderNodeListGenerator = new PreorderNodeListGenerator();
×
771
                        root.accept(preorderNodeListGenerator);
×
772
                        for (org.eclipse.aether.artifact.Artifact artifact : preorderNodeListGenerator.getArtifacts(false)) {
×
773
                            classPath.add(artifact.getFile().toString());
×
774
                        }
×
775
                    } catch (DependencyCollectionException exception) {
×
776
                        throw new MojoExecutionException("Could not collect dependencies for " + mavenCoordinate, exception);
×
777
                    } catch (DependencyResolutionException exception) {
×
778
                        throw new MojoFailureException("Could not resolve dependencies for " + mavenCoordinate, exception);
×
779
                    }
×
780
                }
×
781
            }
782
            return classPath;
×
783
        }
784

785
        /**
786
         * Resolves any implicit dependencies that should be added to the class path.
787
         *
788
         * @return The class path elements of the relevant output directory.
789
         * @throws MojoFailureException If the class loader resolution yields a failure.
790
         */
791
        protected List<String> resolveImplicitClassPathElements() throws MojoFailureException {
792
            return Collections.emptyList();
×
793
        }
794

795
        @Override
796
        protected void apply(List<Transformer> transformers, List<String> elements, Map<Coordinate, String> coordinates) throws MojoExecutionException, MojoFailureException, IOException {
797
            File source = new File(this.source), target = new File(this.target);
×
798
            getLog().info("Transforming " + this.source + " to " + this.target);
×
799
            Plugin.Engine.Source resolved;
800
            if (source.isDirectory()) {
×
801
                resolved = new Plugin.Engine.Source.ForFolder(source);
×
802
            } else if (source.exists()) {
×
803
                resolved = new Plugin.Engine.Source.ForJarFile(source);
×
804
            } else {
805
                throw new MojoFailureException("Source location does not exist: " + source);
×
806
            }
807
            transform(elements,
×
808
                    coordinates,
809
                    transformers,
810
                    resolved,
811
                    target.isDirectory() ? new Plugin.Engine.Target.ForFolder(target) : new Plugin.Engine.Target.ForJarFile(target),
×
812
                    source,
813
                    false);
814
        }
×
815

816
        /**
817
         * Transforms specified classes from files in a folder or a jar file to a folder or jar file. Additionally, all class path dependencies
818
         * will be made visible during plugin application.
819
         */
820
        @Mojo(name = "transform-location", defaultPhase = LifecyclePhase.PROCESS_CLASSES, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE)
821
        public static class WithoutRuntimeDependencies extends ForExplicitLocations {
×
822

823
            @Override
824
            protected List<String> resolveImplicitClassPathElements() throws MojoFailureException {
825
                try {
826
                    return project.getCompileClasspathElements();
×
827
                } catch (DependencyResolutionRequiredException exception) {
×
828
                    throw new MojoFailureException("Could not resolve class path", exception);
×
829
                }
830
            }
831
        }
832

833
        /**
834
         * Transforms specified classes from files in a folder or a jar file to a folder or jar file. Additionally, all class path dependencies
835
         * will be made visible during plugin application, including runtime dependencies.
836
         */
837
        @Mojo(name = "transform-location-runtime", defaultPhase = LifecyclePhase.PROCESS_CLASSES, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
838
        public static class WithRuntimeDependencies extends ForExplicitLocations {
×
839

840
            @Override
841
            protected List<String> resolveImplicitClassPathElements() {
842
                try {
843
                    return project.getRuntimeClasspathElements();
×
844
                } catch (DependencyResolutionRequiredException exception) {
×
845
                    throw new RuntimeException("Could not resolve runtime class path", exception);
×
846
                }
847
            }
848
        }
849

850
        /**
851
         * Transforms specified classes from files in a folder or a jar file to a folder or jar file. Additionally, all class path dependencies
852
         * will be made visible during plugin application, including any non-test dependencies.
853
         */
854
        @Mojo(name = "transform-location-extended", defaultPhase = LifecyclePhase.PROCESS_CLASSES, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
855
        public static class WithExtendedDependencies extends ForExplicitLocations {
×
856

857
            @Override
858
            protected List<String> resolveImplicitClassPathElements() {
859
                List<String> classPath = new ArrayList<String>(project.getArtifacts().size() + 1);
×
860
                String directory = project.getBuild().getOutputDirectory();
×
861
                if (directory != null) {
×
862
                    classPath.add(directory);
×
863
                }
864
                for (Artifact artifact : project.getArtifacts()) {
×
865
                    if (artifact.getArtifactHandler().isAddedToClasspath()
×
866
                            && !Artifact.SCOPE_TEST.equals(artifact.getScope())
×
867
                            && !Artifact.SCOPE_IMPORT.equals(artifact.getScope())) {
×
868
                        File file = artifact.getFile();
×
869
                        if (file != null) {
×
870
                            classPath.add(file.getPath());
×
871
                        }
872
                    }
873
                }
×
874
                return classPath;
×
875
            }
876
        }
877

878
        /**
879
         * Transforms specified classes from files in a folder or a jar file to a folder or jar file. Additionally, all class path dependencies
880
         * will be made visible during plugin application, including any non-test and test dependencies.
881
         */
882
        @Mojo(name = "transform-location-test", defaultPhase = LifecyclePhase.PROCESS_TEST_CLASSES, threadSafe = true, requiresDependencyResolution = ResolutionScope.TEST)
883
        public static class ForTestTypes extends ForExplicitLocations {
×
884

885
            @Override
886
            protected List<String> resolveImplicitClassPathElements() throws MojoFailureException {
887
                try {
888
                    return project.getTestClasspathElements();
×
889
                } catch (DependencyResolutionRequiredException exception) {
×
890
                    throw new MojoFailureException("Could not resolve test class path", exception);
×
891
                }
892
            }
893
        }
894
    }
895

896
    /**
897
     * Transforms all jars for a folder containing jar files, typically project dependencies.
898
     */
899
    @Mojo(name = "transform-dependencies", defaultPhase = LifecyclePhase.PROCESS_CLASSES, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE)
900
    public static class ForDependencyFolder extends ByteBuddyMojo {
×
901

902
        /**
903
         * The source folder that contains the project's dependencies.
904
         */
905
        @UnknownNull
906
        @Parameter(required = true)
907
        public String source;
908

909
        /**
910
         * The target folder that contains the project's dependencies or {@code null} if the {@link ForDependencyFolder#source} folder should be used.
911
         */
912
        @MaybeNull
913
        @Parameter(required = false)
914
        public String target;
915

916
        @Override
917
        protected List<String> resolveClassPathElements(Map<Coordinate, String> coordinates) {
918
            try {
919
                return project.getCompileClasspathElements();
×
920
            } catch (DependencyResolutionRequiredException exception) {
×
921
                throw new RuntimeException("Could not resolve class path", exception);
×
922
            }
923
        }
924

925
        @Override
926
        protected void apply(List<Transformer> transformers, List<String> elements, Map<Coordinate, String> coordinates) throws MojoExecutionException, MojoFailureException, IOException {
927
            File source = new File(this.source), target = this.target == null ? source : new File(this.target);
×
928
            getLog().info("Transforming dependencies in " + this.source + (this.target == null ? "" : (" to " + this.target)));
×
929
            if (!source.isDirectory()) {
×
930
                throw new MojoFailureException("Expected " + this.source + " to be a folder");
×
931
            } else if (this.target != null && target.isFile()) {
×
932
                throw new MojoFailureException("Did not expect " + this.target + " to be a file");
×
933
            }
934
            File[] file = source.listFiles();
×
935
            if (file != null) {
×
936
                for (File aFile : file) {
×
937
                    if (aFile.isFile()) {
×
938
                        transform(elements,
×
939
                                coordinates,
940
                                transformers,
941
                                new Plugin.Engine.Source.ForJarFile(aFile),
942
                                new Plugin.Engine.Target.ForJarFile(new File(target, aFile.getName())),
×
943
                                aFile,
944
                                false);
945
                    }
946
                }
947
            }
948
        }
×
949
    }
950

951
    /**
952
     * A {@link BuildLogger} implementation for a Maven {@link Log}.
953
     */
954
    protected static class MavenBuildLogger implements BuildLogger {
955

956
        /**
957
         * The logger to delegate to.
958
         */
959
        private final Log log;
960

961
        /**
962
         * Creates a new Maven build logger.
963
         *
964
         * @param log The logger to delegate to.
965
         */
966
        protected MavenBuildLogger(Log log) {
1✔
967
            this.log = log;
1✔
968
        }
1✔
969

970
        /**
971
         * {@inheritDoc}
972
         */
973
        public boolean isDebugEnabled() {
974
            return log.isDebugEnabled();
×
975
        }
976

977
        /**
978
         * {@inheritDoc}
979
         */
980
        public void debug(String message) {
981
            log.debug(message);
×
982
        }
×
983

984
        /**
985
         * {@inheritDoc}
986
         */
987
        public void debug(String message, Throwable throwable) {
988
            log.debug(message, throwable);
×
989
        }
×
990

991
        /**
992
         * {@inheritDoc}
993
         */
994
        public boolean isInfoEnabled() {
995
            return log.isInfoEnabled();
×
996
        }
997

998
        /**
999
         * {@inheritDoc}
1000
         */
1001
        public void info(String message) {
1002
            log.info(message);
×
1003
        }
×
1004

1005
        /**
1006
         * {@inheritDoc}
1007
         */
1008
        public void info(String message, Throwable throwable) {
1009
            log.info(message, throwable);
×
1010
        }
×
1011

1012
        /**
1013
         * {@inheritDoc}
1014
         */
1015
        public boolean isWarnEnabled() {
1016
            return log.isWarnEnabled();
×
1017
        }
1018

1019
        /**
1020
         * {@inheritDoc}
1021
         */
1022
        public void warn(String message) {
1023
            log.warn(message);
×
1024
        }
×
1025

1026
        /**
1027
         * {@inheritDoc}
1028
         */
1029
        public void warn(String message, Throwable throwable) {
1030
            log.warn(message, throwable);
×
1031
        }
×
1032

1033
        /**
1034
         * {@inheritDoc}
1035
         */
1036
        public boolean isErrorEnabled() {
1037
            return log.isErrorEnabled();
×
1038
        }
1039

1040
        /**
1041
         * {@inheritDoc}
1042
         */
1043
        public void error(String message) {
1044
            log.error(message);
×
1045
        }
×
1046

1047
        /**
1048
         * {@inheritDoc}
1049
         */
1050
        public void error(String message, Throwable throwable) {
1051
            log.error(message, throwable);
×
1052
        }
×
1053
    }
1054

1055
    /**
1056
     * A {@link Plugin.Engine.Listener} that logs several relevant events during the build.
1057
     */
1058
    protected static class TransformationLogger extends Plugin.Engine.Listener.Adapter {
1059

1060
        /**
1061
         * The logger to delegate to.
1062
         */
1063
        private final Log log;
1064

1065
        /**
1066
         * Creates a new transformation logger.
1067
         *
1068
         * @param log The logger to delegate to.
1069
         */
1070
        protected TransformationLogger(Log log) {
1✔
1071
            this.log = log;
1✔
1072
        }
1✔
1073

1074
        @Override
1075
        public void onTransformation(TypeDescription typeDescription, List<Plugin> plugins) {
1076
            log.debug("Transformed " + typeDescription + " using " + plugins);
1✔
1077
        }
1✔
1078

1079
        @Override
1080
        public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
1081
            log.warn("Failed to transform " + typeDescription + " using " + plugin, throwable);
1✔
1082
        }
1✔
1083

1084
        @Override
1085
        public void onError(Map<TypeDescription, List<Throwable>> throwables) {
1086
            log.warn("Failed to transform " + throwables.size() + " types");
1✔
1087
        }
1✔
1088

1089
        @Override
1090
        public void onError(Plugin plugin, Throwable throwable) {
1091
            log.error("Failed to close " + plugin, throwable);
×
1092
        }
×
1093

1094
        @Override
1095
        public void onLiveInitializer(TypeDescription typeDescription, TypeDescription definingType) {
1096
            log.debug("Discovered live initializer for " + definingType + " as a result of transforming " + typeDescription);
1✔
1097
        }
1✔
1098
    }
1099

1100
    /**
1101
     * A transformer that is applied during the plugin's execution.
1102
     */
1103
    protected abstract static class Transformer {
1✔
1104

1105
        /**
1106
         * Returns the name of the plugin to apply.
1107
         *
1108
         * @return The name of the plugin to apply.
1109
         * @throws MojoExecutionException If the plugin name was not set.
1110
         */
1111
        protected abstract String getPlugin() throws MojoExecutionException;
1112

1113
        /**
1114
         * Returns the argument resolvers to use.
1115
         *
1116
         * @return The argument resolvers to use.
1117
         */
1118
        protected abstract List<? extends Plugin.Factory.UsingReflection.ArgumentResolver> toArgumentResolvers();
1119

1120
        /**
1121
         * Resolves the class loader to use for resolving the plugin.
1122
         *
1123
         * @param classLoaderResolver The class loader resolver to use.
1124
         * @param coordinates         The managed coordinates of this project.
1125
         * @param groupId             The group id of this project.
1126
         * @param artifactId          The artifact id of this project.
1127
         * @param version             The version of this project.
1128
         * @param packaging           The packaging of this project.
1129
         * @return The class loader to use.
1130
         * @throws MojoFailureException   If the class loader resolution yields a failure.
1131
         * @throws MojoExecutionException The class loader resolution is incorrect.
1132
         */
1133
        protected abstract ClassLoader toClassLoader(ClassLoaderResolver classLoaderResolver, Map<Coordinate, String> coordinates, String groupId, String artifactId, String version, String packaging) throws MojoFailureException, MojoExecutionException;
1134

1135
        /**
1136
         * A transformer for an explicitly configured plugin.
1137
         */
1138
        protected static class ForConfiguredPlugin extends Transformer {
1139

1140
            /**
1141
             * The configured transformation.
1142
             */
1143
            private final Transformation transformation;
1144

1145
            /**
1146
             * Creates a new transformer for an explicitly configured plugin.
1147
             *
1148
             * @param transformation The configured transformation.
1149
             */
1150
            protected ForConfiguredPlugin(Transformation transformation) {
1✔
1151
                this.transformation = transformation;
1✔
1152
            }
1✔
1153

1154
            @Override
1155
            protected String getPlugin() throws MojoExecutionException {
1156
                return transformation.getPlugin();
1✔
1157
            }
1158

1159
            @Override
1160
            protected List<? extends Plugin.Factory.UsingReflection.ArgumentResolver> toArgumentResolvers() {
1161
                return transformation.makeArgumentResolvers();
1✔
1162
            }
1163

1164
            @Override
1165
            protected ClassLoader toClassLoader(ClassLoaderResolver classLoaderResolver, Map<Coordinate, String> coordinates, String groupId, String artifactId, String version, String packaging) throws MojoFailureException, MojoExecutionException {
1166
                String managed = coordinates.get(new Coordinate(transformation.getGroupId(groupId), transformation.getArtifactId(artifactId)));
1✔
1167
                return classLoaderResolver.resolve(transformation.asCoordinate(groupId, artifactId, managed == null ? version : managed, packaging));
1✔
1168
            }
1169
        }
1170

1171
        /**
1172
         * A transformer for a discovered plugin.
1173
         */
1174
        protected static class ForDiscoveredPlugin extends Transformer {
1175

1176
            /**
1177
             * The name of the discovered plugin.
1178
             */
1179
            private final String plugin;
1180

1181
            /**
1182
             * Creates a new transformer for a discovered plugin.
1183
             *
1184
             * @param plugin The name of the discovered plugin.
1185
             */
1186
            protected ForDiscoveredPlugin(String plugin) {
×
1187
                this.plugin = plugin;
×
1188
            }
×
1189

1190
            @Override
1191
            protected String getPlugin() {
1192
                return plugin;
×
1193
            }
1194

1195
            @Override
1196
            protected List<? extends Plugin.Factory.UsingReflection.ArgumentResolver> toArgumentResolvers() {
1197
                return Collections.emptyList();
×
1198
            }
1199

1200
            @Override
1201
            protected ClassLoader toClassLoader(ClassLoaderResolver classLoaderResolver, Map<Coordinate, String> coordinates, String groupId, String artifactId, String version, String packaging) {
1202
                return ByteBuddyMojo.class.getClassLoader();
×
1203
            }
1204

1205
            /**
1206
             * A transformer for a discovered plugin from the class path.
1207
             */
1208
            protected static class FromClassLoader extends ForDiscoveredPlugin {
1209

1210
                /**
1211
                 * The class path elements for loading this plugin.
1212
                 */
1213
                private final List<String> classPath;
1214

1215
                /**
1216
                 * Creates a new transformer for a discovered plugin from the class path.
1217
                 *
1218
                 * @param plugin    The name of the discovered plugin.
1219
                 * @param classPath The class path elements for loading this plugin.
1220
                 */
1221
                protected FromClassLoader(String plugin, List<String> classPath) {
1222
                    super(plugin);
×
1223
                    this.classPath = classPath;
×
1224
                }
×
1225

1226
                @Override
1227
                @SuppressFBWarnings(value = "DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED", justification = "The security manager is not normally used within Maven.")
1228
                protected ClassLoader toClassLoader(ClassLoaderResolver classLoaderResolver, Map<Coordinate, String> coordinates, String groupId, String artifactId, String version, String packaging) {
1229
                    URL[] url = new URL[classPath.size()];
×
1230
                    for (int index = 0; index < classPath.size(); index++) {
×
1231
                        try {
1232
                            url[index] = new File(classPath.get(index)).toURI().toURL();
×
1233
                        } catch (MalformedURLException exception) {
×
1234
                            throw new IllegalStateException("Failed to resolve class path element to URL: " + classPath.get(index), exception);
×
1235
                        }
×
1236
                    }
1237
                    return new URLClassLoader(url, ByteBuddyMojo.class.getClassLoader());
×
1238
                }
1239
            }
1240
        }
1241
    }
1242

1243
    /**
1244
     * A coordinate to locate a managed dependency.
1245
     */
1246
    protected static class Coordinate {
1247

1248
        /**
1249
         * The managed dependency's group id.
1250
         */
1251
        private final String groupId;
1252

1253
        /**
1254
         * The managed dependency's artifact id.
1255
         */
1256
        private final String artifactId;
1257

1258
        /**
1259
         * Creates a new coordinate for a managed dependency.
1260
         *
1261
         * @param groupId    The managed depencency's group id.
1262
         * @param artifactId The managed depencency's artifact id.
1263
         */
1264
        protected Coordinate(String groupId, String artifactId) {
1✔
1265
            this.groupId = groupId;
1✔
1266
            this.artifactId = artifactId;
1✔
1267
        }
1✔
1268

1269
        @Override
1270
        public int hashCode() {
1271
            int result = groupId.hashCode();
1✔
1272
            result = 31 * result + artifactId.hashCode();
1✔
1273
            return result;
1✔
1274
        }
1275

1276
        @Override
1277
        public boolean equals(Object other) {
1278
            if (this == other) return true;
×
1279
            if (other == null || getClass() != other.getClass()) return false;
×
1280

1281
            Coordinate that = (Coordinate) other;
×
1282

1283
            if (!groupId.equals(that.groupId)) return false;
×
1284
            return artifactId.equals(that.artifactId);
×
1285
        }
1286
    }
1287

1288
    /**
1289
     * A filter for files that were written before a given timestamp, to avoid duplicate application.
1290
     */
1291
    protected static class StalenessFilter extends ElementMatcher.Junction.ForNonNullValues<Plugin.Engine.Source.Element> {
1292

1293
        /**
1294
         * The logger to use.
1295
         */
1296
        private final Log log;
1297

1298
        /**
1299
         * The timestamp for files to be filtered if they were created before it.
1300
         */
1301
        private final long latestTimestamp;
1302

1303
        /**
1304
         * A count of class files that were filtered.
1305
         */
1306
        private int filtered;
1307

1308
        /**
1309
         * Creates a new staleness filter.
1310
         *
1311
         * @param log             The logger to use.
1312
         * @param latestTimestamp The timestamp for files to be filtered if they were created before it.
1313
         */
1314
        protected StalenessFilter(Log log, long latestTimestamp) {
×
1315
            this.log = log;
×
1316
            this.latestTimestamp = latestTimestamp;
×
1317
        }
×
1318

1319
        /**
1320
         * {@inheritDoc}
1321
         */
1322
        protected boolean doMatch(Plugin.Engine.Source.Element target) {
1323
            File file = target.resolveAs(File.class);
×
1324
            if (file == null) {
×
1325
                throw new IllegalStateException("Expected " + target + " to resolve to a file");
×
1326
            }
1327
            if (file.lastModified() < latestTimestamp) {
×
1328
                filtered += 1;
×
1329
                log.debug("Filtering " + file + " due to staleness: " + file.lastModified());
×
1330
                return false;
×
1331
            } else {
1332
                return true;
×
1333
            }
1334
        }
1335

1336
        /**
1337
         * Returns a count of class files that were filtered as they were created prior to the last build.
1338
         *
1339
         * @return The amount of filtered classes.
1340
         */
1341
        protected int getFiltered() {
1342
            return filtered;
×
1343
        }
1344
    }
1345
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc