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

raphw / byte-buddy / #811

03 Nov 2025 10:41AM UTC coverage: 83.677% (-0.3%) from 84.023%
#811

push

raphw
Fix null handling.

0 of 1 new or added line in 1 file covered. (0.0%)

791 existing lines in 12 files now uncovered.

29814 of 35630 relevant lines covered (83.68%)

0.84 hits per line

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

36.86
/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 Maven project.
84
     */
85
    @UnknownNull
86
    @Parameter(defaultValue = "${project}", readonly = true)
87
    public MavenProject project;
88

89
    /**
90
     * The current execution of this plugin.
91
     */
92
    @UnknownNull
93
    @Parameter(defaultValue = "${mojoExecution}", readonly = true)
94
    public MojoExecution execution;
95

96
    /**
97
     * The currently used repository system.
98
     */
99
    @UnknownNull
100
    @Component
101
    public RepositorySystem repositorySystem;
102

103
    /**
104
     * The currently used system session for the repository system.
105
     */
106
    @MaybeNull
107
    @Parameter(defaultValue = "${repositorySystemSession}", readonly = true)
108
    public RepositorySystemSession repositorySystemSession;
109

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

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

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

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

176
    /**
177
     * When set to {@code true}, this mojo is not applied to the current module.
178
     */
179
    @Parameter(defaultValue = "false", required = true)
180
    public boolean skip;
181

182
    /**
183
     * When set to {@code true}, this mojo warns of an non-existent output directory.
184
     */
185
    @Parameter(defaultValue = "true", required = true)
186
    public boolean warnOnMissingOutputDirectory;
187

188
    /**
189
     * When set to {@code true}, this mojo warns of not having transformed any types.
190
     */
191
    @Parameter(defaultValue = "true", required = true)
192
    public boolean warnOnEmptyTypeSet;
193

194
    /**
195
     * When set to {@code true}, this mojo fails immediately if a plugin cannot be applied.
196
     */
197
    @Parameter(defaultValue = "true", required = true)
198
    public boolean failFast;
199

200
    /**
201
     * When set to {@code true}, the debug information of class files should be parsed to extract parameter names.
202
     */
203
    @Parameter(defaultValue = "false", required = true)
204
    public boolean extendedParsing;
205

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

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

225
    /**
226
     * Indicates the amount of threads used for parallel type processing or {@code 0} for serial processing.
227
     */
228
    @Parameter(defaultValue = "0", required = true)
229
    public int threads;
230

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

238
    /**
239
     * Defines the version to use for resolving multi-release jar files. If not set, the Java compile version is used.
240
     */
241
    @MaybeNull
242
    @Parameter
243
    public Integer multiReleaseVersion;
244

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

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

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

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

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

507
    /**
508
     * Matches elements which represent a Java class that is represented in the list or an inner class of the classes represented in the list.
509
     */
510
    private static class FilePrefixMatcher extends ElementMatcher.Junction.ForNonNullValues<Plugin.Engine.Source.Element> {
511

512
        /**
513
         * A list of names to match.
514
         */
515
        private final List<String> names;
516

517
        /**
518
         * Create a new matcher for a list of names.
519
         *
520
         * @param names A list of included names.
521
         */
UNCOV
522
        private FilePrefixMatcher(List<String> names) {
×
UNCOV
523
            this.names = names;
×
UNCOV
524
        }
×
525

526
        /**
527
         * {@inheritDoc}
528
         */
529
        protected boolean doMatch(Plugin.Engine.Source.Element target) {
UNCOV
530
            for (String name : names) {
×
UNCOV
531
                if (target.getName().equals(name + ClassFileLocator.CLASS_FILE_EXTENSION)
×
UNCOV
532
                        || target.getName().startsWith(name + "$")
×
UNCOV
533
                        && target.getName().endsWith(ClassFileLocator.CLASS_FILE_EXTENSION)) {
×
UNCOV
534
                    return true;
×
535
                }
536
            }
×
537
            return false;
×
538
        }
539
    }
540

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

546
        /**
547
         * The build context to support incremental builds.
548
         */
549
        @MaybeNull
550
        @Component
551
        public BuildContext context;
552

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

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

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

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

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

621
            @Override
622
            protected String getOutputDirectory() {
623
                return project.getBuild().getOutputDirectory();
1✔
624
            }
625

626
            @MaybeNull
627
            @Override
628
            protected String getSourceDirectory() {
629
                return project.getBuild().getSourceDirectory();
1✔
630
            }
631

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

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

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

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

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

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

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

700
            @Override
701
            protected String getOutputDirectory() {
UNCOV
702
                return project.getBuild().getTestOutputDirectory();
×
703
            }
704

705
            @MaybeNull
706
            @Override
707
            protected String getSourceDirectory() {
UNCOV
708
                return project.getBuild().getTestSourceDirectory();
×
709
            }
710

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

948
    /**
949
     * A {@link BuildLogger} implementation for a Maven {@link Log}.
950
     */
951
    protected static class MavenBuildLogger implements BuildLogger {
952

953
        /**
954
         * The logger to delegate to.
955
         */
956
        private final Log log;
957

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

967
        /**
968
         * {@inheritDoc}
969
         */
970
        public boolean isDebugEnabled() {
UNCOV
971
            return log.isDebugEnabled();
×
972
        }
973

974
        /**
975
         * {@inheritDoc}
976
         */
977
        public void debug(String message) {
UNCOV
978
            log.debug(message);
×
UNCOV
979
        }
×
980

981
        /**
982
         * {@inheritDoc}
983
         */
984
        public void debug(String message, Throwable throwable) {
UNCOV
985
            log.debug(message, throwable);
×
UNCOV
986
        }
×
987

988
        /**
989
         * {@inheritDoc}
990
         */
991
        public boolean isInfoEnabled() {
UNCOV
992
            return log.isInfoEnabled();
×
993
        }
994

995
        /**
996
         * {@inheritDoc}
997
         */
998
        public void info(String message) {
UNCOV
999
            log.info(message);
×
UNCOV
1000
        }
×
1001

1002
        /**
1003
         * {@inheritDoc}
1004
         */
1005
        public void info(String message, Throwable throwable) {
UNCOV
1006
            log.info(message, throwable);
×
UNCOV
1007
        }
×
1008

1009
        /**
1010
         * {@inheritDoc}
1011
         */
1012
        public boolean isWarnEnabled() {
UNCOV
1013
            return log.isWarnEnabled();
×
1014
        }
1015

1016
        /**
1017
         * {@inheritDoc}
1018
         */
1019
        public void warn(String message) {
UNCOV
1020
            log.warn(message);
×
UNCOV
1021
        }
×
1022

1023
        /**
1024
         * {@inheritDoc}
1025
         */
1026
        public void warn(String message, Throwable throwable) {
UNCOV
1027
            log.warn(message, throwable);
×
UNCOV
1028
        }
×
1029

1030
        /**
1031
         * {@inheritDoc}
1032
         */
1033
        public boolean isErrorEnabled() {
UNCOV
1034
            return log.isErrorEnabled();
×
1035
        }
1036

1037
        /**
1038
         * {@inheritDoc}
1039
         */
1040
        public void error(String message) {
UNCOV
1041
            log.error(message);
×
UNCOV
1042
        }
×
1043

1044
        /**
1045
         * {@inheritDoc}
1046
         */
1047
        public void error(String message, Throwable throwable) {
UNCOV
1048
            log.error(message, throwable);
×
UNCOV
1049
        }
×
1050
    }
1051

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

1057
        /**
1058
         * The logger to delegate to.
1059
         */
1060
        private final Log log;
1061

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

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

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

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

1086
        @Override
1087
        public void onError(Plugin plugin, Throwable throwable) {
UNCOV
1088
            log.error("Failed to close " + plugin, throwable);
×
UNCOV
1089
        }
×
1090

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

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

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

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

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

1132
        /**
1133
         * A transformer for an explicitly configured plugin.
1134
         */
1135
        protected static class ForConfiguredPlugin extends Transformer {
1136

1137
            /**
1138
             * The configured transformation.
1139
             */
1140
            private final Transformation transformation;
1141

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

1151
            @Override
1152
            protected String getPlugin() throws MojoExecutionException {
1153
                return transformation.getPlugin();
1✔
1154
            }
1155

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

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

1168
        /**
1169
         * A transformer for a discovered plugin.
1170
         */
1171
        protected static class ForDiscoveredPlugin extends Transformer {
1172

1173
            /**
1174
             * The name of the discovered plugin.
1175
             */
1176
            private final String plugin;
1177

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

1187
            @Override
1188
            protected String getPlugin() {
UNCOV
1189
                return plugin;
×
1190
            }
1191

1192
            @Override
1193
            protected List<? extends Plugin.Factory.UsingReflection.ArgumentResolver> toArgumentResolvers() {
UNCOV
1194
                return Collections.emptyList();
×
1195
            }
1196

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

1202
            /**
1203
             * A transformer for a discovered plugin from the class path.
1204
             */
1205
            protected static class FromClassLoader extends ForDiscoveredPlugin {
1206

1207
                /**
1208
                 * The class path elements for loading this plugin.
1209
                 */
1210
                private final List<String> classPath;
1211

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

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

1240
    /**
1241
     * A coordinate to locate a managed dependency.
1242
     */
1243
    protected static class Coordinate {
1244

1245
        /**
1246
         * The managed dependency's group id.
1247
         */
1248
        private final String groupId;
1249

1250
        /**
1251
         * The managed dependency's artifact id.
1252
         */
1253
        private final String artifactId;
1254

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

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

1273
        @Override
1274
        public boolean equals(Object other) {
UNCOV
1275
            if (this == other) return true;
×
UNCOV
1276
            if (other == null || getClass() != other.getClass()) return false;
×
1277

1278
            Coordinate that = (Coordinate) other;
×
1279

UNCOV
1280
            if (!groupId.equals(that.groupId)) return false;
×
1281
            return artifactId.equals(that.artifactId);
×
1282
        }
1283
    }
1284

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

1290
        /**
1291
         * The logger to use.
1292
         */
1293
        private final Log log;
1294

1295
        /**
1296
         * The timestamp for files to be filtered if they were created before it.
1297
         */
1298
        private final long latestTimestamp;
1299

1300
        /**
1301
         * A count of class files that were filtered.
1302
         */
1303
        private int filtered;
1304

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

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

1333
        /**
1334
         * Returns a count of class files that were filtered as they were created prior to the last build.
1335
         *
1336
         * @return The amount of filtered classes.
1337
         */
1338
        protected int getFiltered() {
UNCOV
1339
            return filtered;
×
1340
        }
1341
    }
1342
}
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