Skip to content

Commit 3a4d654

Browse files
committed
[MPLUGIN-508] Upgrade to 4.0.0-beta-3
1 parent 8be1705 commit 3a4d654

File tree

22 files changed

+781
-150
lines changed

22 files changed

+781
-150
lines changed

.github/workflows/maven-verify.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,8 @@ jobs:
2525
build:
2626
name: Verify
2727
uses: apache/maven-gh-actions-shared/.github/workflows/maven-verify.yml@v4
28+
with:
29+
maven-matrix: '[ "3.6.3", "3.9.7", "4.0.0-beta-3" ]'
30+
jdk-matrix: '[ "8", "17", "21" ]'
31+
matrix-exclude: '[ { maven: "4.0.0-beta-3", jdk: "8" } ]'
32+

maven-plugin-plugin/src/it/v4api/invoker.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@
1515
# specific language governing permissions and limitations
1616
# under the License.
1717

18+
invoker.java.version = 17+
1819
invoker.goals.1 = clean install

maven-plugin-plugin/src/it/v4api/src/main/java/org/apache/maven/its/v4api/FirstMojo.java

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,12 @@
2222

2323
import org.apache.maven.api.MojoExecution;
2424
import org.apache.maven.api.Project;
25-
import org.apache.maven.api.ResolutionScope;
2625
import org.apache.maven.api.Session;
26+
import org.apache.maven.api.di.Inject;
27+
import org.apache.maven.api.di.Named;
2728
import org.apache.maven.api.plugin.Log;
2829
import org.apache.maven.api.plugin.MojoException;
29-
import org.apache.maven.api.plugin.annotations.Component;
3030
import org.apache.maven.api.plugin.annotations.Execute;
31-
import org.apache.maven.api.plugin.annotations.LifecyclePhase;
3231
import org.apache.maven.api.plugin.annotations.Mojo;
3332
import org.apache.maven.api.plugin.annotations.Parameter;
3433
import org.apache.maven.api.services.ArtifactInstaller;
@@ -38,15 +37,12 @@
3837
* Test mojo for the v4 api plugin descriptor generation.
3938
* This mojo is not actually runnable because:
4039
* - it's using a custom lifecycle which is not defined
41-
* - it has a @Component dependency on ArtifactInstaller (hint=test) which does not exist
40+
* - it has a @Inject dependency on ArtifactInstaller (hint=test) which does not exist
4241
*
4342
* @since 1.2
4443
*/
45-
@Mojo(
46-
name = "first",
47-
requiresDependencyResolution = ResolutionScope.TEST,
48-
defaultPhase = LifecyclePhase.INTEGRATION_TEST)
49-
@Execute(phase = LifecyclePhase.GENERATE_SOURCES, lifecycle = "cobertura")
44+
@Mojo(name = "first", defaultPhase = "integration-test")
45+
@Execute(phase = "generate-sources", lifecycle = "cobertura")
5046
public class FirstMojo implements org.apache.maven.api.plugin.Mojo {
5147

5248
/**
@@ -66,23 +62,24 @@ public class FirstMojo implements org.apache.maven.api.plugin.Mojo {
6662
@Parameter(name = "namedParam", alias = "alias")
6763
private String aliasedParam;
6864

69-
@Component
65+
@Inject
7066
private Session session;
7167

72-
@Component
68+
@Inject
7369
private Project project;
7470

75-
@Component
71+
@Inject
7672
private MojoExecution mojo;
7773

78-
@Component
74+
@Inject
7975
private Settings settings;
8076

81-
@Component
77+
@Inject
8278
private Log log;
8379

84-
@Component(role = ArtifactInstaller.class, hint = "test")
85-
private Object custom;
80+
@Inject
81+
@Named("test")
82+
private ArtifactInstaller custom;
8683

8784
public void execute() throws MojoException {}
8885
}

maven-plugin-plugin/src/it/v4api/verify.groovy

Lines changed: 10 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -24,60 +24,21 @@ assert descriptorFile.isFile()
2424

2525
def pluginDescriptor = new XmlParser().parse( descriptorFile );
2626

27-
assert pluginDescriptor.requiredJavaVersion.text() == '1.8'
28-
assert pluginDescriptor.requiredMavenVersion.text() == '4.0.0-alpha-4'
27+
assert pluginDescriptor.requiredJavaVersion.text() == '17'
28+
assert pluginDescriptor.requiredMavenVersion.text() == '4.0.0-beta-3'
2929

3030
def mojo = pluginDescriptor.mojos.mojo.findAll{ it.goal.text() == "first" }[0]
3131

3232
assert mojo.goal.text() == 'first'
3333
assert mojo.implementation.text() == 'org.apache.maven.its.v4api.FirstMojo'
3434
assert mojo.language.text() == 'java'
3535
assert mojo.description.text().startsWith('Test mojo for the v4 api plugin descriptor generation.')
36-
assert mojo.requiresDependencyResolution.text() == 'test'
37-
assert mojo.requiresDependencyCollection.text() == ''
38-
assert mojo.requiresProject.text() == 'true'
39-
assert mojo.requiresOnline.text() == 'false'
40-
assert mojo.requiresDirectInvocation.text() == 'false'
36+
assert mojo.projectRequired.text() == 'true'
37+
assert mojo.onlineRequired.text() == 'false'
4138
assert mojo.aggregator.text() == 'false'
42-
assert mojo.threadSafe.text() == 'false'
4339
assert mojo.phase.text() == 'integration-test'
4440
assert mojo.executePhase.text() == 'generate-sources'
4541
assert mojo.executeLifecycle.text() == 'cobertura'
46-
assert mojo.v4Api.text() == 'true'
47-
48-
assert mojo.configuration.basedir[0].text() == ''
49-
assert mojo.configuration.basedir[0].'@implementation' == 'java.nio.file.Path'
50-
assert mojo.configuration.basedir[0].'@default-value' == '${basedir}'
51-
52-
assert mojo.configuration.touchFile[0].text() == '${first.touchFile}'
53-
assert mojo.configuration.touchFile[0].'@implementation' == 'java.nio.file.Path'
54-
assert mojo.configuration.touchFile[0].'@default-value' == '${project.build.directory}/touch.txt'
55-
56-
assert mojo.requirements.requirement.size() == 6
57-
58-
assert mojo.requirements.requirement[0].role.text() == 'org.apache.maven.api.services.ArtifactInstaller'
59-
assert mojo.requirements.requirement[0].'role-hint'.text() == 'test'
60-
assert mojo.requirements.requirement[0].'field-name'.text() == 'custom'
61-
62-
assert mojo.requirements.requirement[1].role.text() == 'org.apache.maven.api.plugin.Log'
63-
assert mojo.requirements.requirement[1].'role-hint'.isEmpty()
64-
assert mojo.requirements.requirement[1].'field-name'.text() == 'log'
65-
66-
assert mojo.requirements.requirement[2].role.text() == 'org.apache.maven.api.MojoExecution'
67-
assert mojo.requirements.requirement[2].'role-hint'.isEmpty()
68-
assert mojo.requirements.requirement[2].'field-name'.text() == 'mojo'
69-
70-
assert mojo.requirements.requirement[3].role.text() == 'org.apache.maven.api.Project'
71-
assert mojo.requirements.requirement[3].'role-hint'.isEmpty()
72-
assert mojo.requirements.requirement[3].'field-name'.text() == 'project'
73-
74-
assert mojo.requirements.requirement[4].role.text() == 'org.apache.maven.api.Session'
75-
assert mojo.requirements.requirement[4].'role-hint'.isEmpty()
76-
assert mojo.requirements.requirement[4].'field-name'.text() == 'session'
77-
78-
assert mojo.requirements.requirement[5].role.text() == 'org.apache.maven.api.settings.Settings'
79-
assert mojo.requirements.requirement[5].'role-hint'.isEmpty()
80-
assert mojo.requirements.requirement[5].'field-name'.text() == 'settings'
8142

8243
assert mojo.parameters.parameter.size() == 3
8344

@@ -90,6 +51,8 @@ assert parameter.deprecated.isEmpty()
9051
assert parameter.required.text() == 'false'
9152
assert parameter.editable.text() == 'false'
9253
assert parameter.description.text() == 'Project directory.'
54+
assert parameter.defaultValue.text() == '${basedir}'
55+
assert parameter.expression.isEmpty()
9356

9457
parameter = mojo.parameters.parameter.findAll{ it.name.text() == "touchFile" }[0]
9558
assert parameter.name.text() == 'touchFile'
@@ -100,6 +63,8 @@ assert parameter.deprecated.isEmpty()
10063
assert parameter.required.text() == 'true'
10164
assert parameter.editable.text() == 'true'
10265
assert parameter.description.text() == ''
66+
assert parameter.defaultValue.text() == '${project.build.directory}/touch.txt'
67+
assert parameter.expression.text() == '${first.touchFile}'
10368

10469
parameter = mojo.parameters.parameter.findAll{ it.name.text() == "namedParam" }[0]
10570
assert parameter.name.text() == 'namedParam'
@@ -110,6 +75,8 @@ assert parameter.deprecated.text() == 'As of 0.2'
11075
assert parameter.required.text() == 'false'
11176
assert parameter.editable.text() == 'true'
11277
assert parameter.description.text() == ''
78+
assert parameter.defaultValue.isEmpty()
79+
assert parameter.expression.isEmpty()
11380

11481
// check help mojo source and class
11582
assert new File( basedir, "target/classes/org/apache/maven/its/v4api/HelpMojo.class" ).isFile()

maven-plugin-plugin/src/main/java/org/apache/maven/plugin/plugin/DescriptorGeneratorMojo.java

Lines changed: 151 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,32 @@
1919
package org.apache.maven.plugin.plugin;
2020

2121
import java.io.File;
22+
import java.io.IOException;
23+
import java.io.InputStream;
24+
import java.io.OutputStream;
2225
import java.net.URI;
23-
import java.util.Arrays;
24-
import java.util.Collections;
25-
import java.util.LinkedHashSet;
26-
import java.util.List;
27-
import java.util.Set;
26+
import java.nio.charset.StandardCharsets;
27+
import java.nio.file.Files;
28+
import java.nio.file.Path;
29+
import java.util.*;
30+
import java.util.stream.Collectors;
31+
import java.util.stream.Stream;
2832

2933
import org.apache.maven.artifact.Artifact;
3034
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
3135
import org.apache.maven.artifact.resolver.filter.IncludesArtifactFilter;
3236
import org.apache.maven.execution.MavenSession;
3337
import org.apache.maven.plugin.MojoExecutionException;
3438
import org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException;
39+
import org.apache.maven.plugin.descriptor.MojoDescriptor;
3540
import org.apache.maven.plugin.descriptor.PluginDescriptor;
3641
import org.apache.maven.plugins.annotations.Component;
3742
import org.apache.maven.plugins.annotations.LifecyclePhase;
3843
import org.apache.maven.plugins.annotations.Mojo;
3944
import org.apache.maven.plugins.annotations.Parameter;
4045
import org.apache.maven.plugins.annotations.ResolutionScope;
4146
import org.apache.maven.tools.plugin.DefaultPluginToolsRequest;
47+
import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
4248
import org.apache.maven.tools.plugin.ExtendedPluginDescriptor;
4349
import org.apache.maven.tools.plugin.PluginToolsRequest;
4450
import org.apache.maven.tools.plugin.extractor.ExtractionException;
@@ -48,8 +54,14 @@
4854
import org.apache.maven.tools.plugin.scanner.MojoScanner;
4955
import org.codehaus.plexus.component.repository.ComponentDependency;
5056
import org.codehaus.plexus.util.ReaderFactory;
57+
import org.codehaus.plexus.util.io.CachingOutputStream;
58+
import org.codehaus.plexus.util.io.CachingWriter;
59+
import org.objectweb.asm.*;
5160
import org.sonatype.plexus.build.incremental.BuildContext;
5261

62+
import static org.objectweb.asm.Opcodes.*;
63+
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
64+
5365
/**
5466
* <p>
5567
* Generate a plugin descriptor.
@@ -78,6 +90,12 @@ public class DescriptorGeneratorMojo extends AbstractGeneratorMojo {
7890
@Parameter(defaultValue = "${project.build.outputDirectory}/META-INF/maven", readonly = true)
7991
private File outputDirectory;
8092

93+
/**
94+
* The directory where the generated class files will be put.
95+
*/
96+
@Parameter(defaultValue = "${project.build.outputDirectory}", readonly = true)
97+
private File classesOutputDirectory;
98+
8199
/**
82100
* The file encoding of the source files.
83101
*
@@ -269,7 +287,6 @@ public class DescriptorGeneratorMojo extends AbstractGeneratorMojo {
269287
protected BuildContext buildContext;
270288

271289
public void generate() throws MojoExecutionException {
272-
273290
if (!"maven-plugin".equalsIgnoreCase(project.getArtifactId())
274291
&& project.getArtifactId().toLowerCase().startsWith("maven-")
275292
&& project.getArtifactId().toLowerCase().endsWith("-plugin")
@@ -352,6 +369,12 @@ public void generate() throws MojoExecutionException {
352369
PluginDescriptorFilesGenerator pluginDescriptorGenerator = new PluginDescriptorFilesGenerator();
353370
pluginDescriptorGenerator.execute(outputDirectory, request);
354371

372+
// Generate the additional factories for v4 mojos
373+
generateFactories(request.getPluginDescriptor());
374+
375+
// Generate index for v4 beans
376+
generateIndex();
377+
355378
buildContext.refresh(outputDirectory);
356379
} catch (GeneratorException e) {
357380
throw new MojoExecutionException("Error writing plugin descriptor", e);
@@ -367,6 +390,128 @@ public void generate() throws MojoExecutionException {
367390
}
368391
}
369392

393+
private void generateIndex() throws GeneratorException {
394+
try {
395+
Set<String> diBeans = new TreeSet<>();
396+
try (Stream<Path> paths = Files.walk(classesOutputDirectory.toPath())) {
397+
List<Path> classes = paths.filter(
398+
p -> p.getFileName().toString().endsWith(".class"))
399+
.collect(Collectors.toList());
400+
for (Path classFile : classes) {
401+
String fileString = classFile.toString();
402+
String className = fileString
403+
.substring(0, fileString.length() - ".class".length())
404+
.replace('/', '.');
405+
try (InputStream is = Files.newInputStream(classFile)) {
406+
ClassReader rdr = new ClassReader(is);
407+
rdr.accept(
408+
new ClassVisitor(Opcodes.ASM9) {
409+
String className;
410+
411+
@Override
412+
public void visit(
413+
int version,
414+
int access,
415+
String name,
416+
String signature,
417+
String superName,
418+
String[] interfaces) {
419+
super.visit(version, access, name, signature, superName, interfaces);
420+
className = name;
421+
}
422+
423+
@Override
424+
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
425+
if ("Lorg/apache/maven/api/di/Named;".equals(descriptor)) {
426+
diBeans.add(className.replace('/', '.'));
427+
}
428+
return null;
429+
}
430+
},
431+
ClassReader.SKIP_FRAMES | ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG);
432+
}
433+
434+
// Class<?> clazz = project.getClassRealm().loadClass(className);
435+
// boolean hasQualifier = Stream.of(clazz.getAnnotations())
436+
// .flatMap(ann -> Stream.of(ann.getClass().getAnnotations()))
437+
// .anyMatch(ann -> "org.apache.maven.api.di.Qualifier"
438+
// .equals(ann.annotationType().getName()));
439+
// if (hasQualifier) {
440+
// diBeans.add(className);
441+
// }
442+
}
443+
}
444+
Path path = outputDirectory.toPath().resolve("org.apache.maven.api.di.Inject");
445+
if (diBeans.isEmpty()) {
446+
Files.deleteIfExists(path);
447+
} else {
448+
String nl = System.lineSeparator();
449+
try (CachingWriter w = new CachingWriter(path, StandardCharsets.UTF_8)) {
450+
String content = diBeans.stream().collect(Collectors.joining(nl, "", nl));
451+
w.write(content);
452+
}
453+
}
454+
} catch (Exception e) {
455+
throw new GeneratorException("Unable to generate index for v4 beans", e);
456+
}
457+
}
458+
459+
private void generateFactories(PluginDescriptor pd) throws GeneratorException {
460+
try {
461+
for (MojoDescriptor md : pd.getMojos()) {
462+
if (md instanceof ExtendedMojoDescriptor && ((ExtendedMojoDescriptor) md).isV4Api()) {
463+
generateFactory(md);
464+
}
465+
}
466+
} catch (IOException e) {
467+
throw new GeneratorException("Unable to generate factories for v4 mojos", e);
468+
}
469+
}
470+
471+
private void generateFactory(MojoDescriptor md) throws IOException {
472+
String mojoClassName = md.getImplementation();
473+
String packageName = mojoClassName.substring(0, mojoClassName.lastIndexOf('.'));
474+
String generatorClassName = mojoClassName.substring(mojoClassName.lastIndexOf('.') + 1) + "Factory";
475+
String mojoName = md.getId();
476+
477+
getLog().debug("Generating v4 factory for " + mojoClassName);
478+
479+
byte[] bin = computeGeneratorClassBytes(packageName, generatorClassName, mojoName, mojoClassName);
480+
481+
try (OutputStream os = new CachingOutputStream(classesOutputDirectory
482+
.toPath()
483+
.resolve(packageName.replace('.', '/') + "/" + generatorClassName + ".class"))) {
484+
os.write(bin);
485+
}
486+
}
487+
488+
static byte[] computeGeneratorClassBytes(
489+
String packageName, String generatorClassName, String mojoName, String mojoClassName) {
490+
String mojo = mojoClassName.replace('.', '/');
491+
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
492+
cw.visitSource(generatorClassName + ".java", null);
493+
AnnotationVisitor av = cw.visitAnnotation("Lorg/apache/maven/api/di/Named;", true);
494+
av.visit("value", mojoName);
495+
av.visitEnd();
496+
cw.visitAnnotation("Lorg/apache/maven/api/annotations/Generated;", true).visitEnd();
497+
cw.visit(
498+
V1_8,
499+
ACC_PUBLIC + ACC_SUPER + ACC_SYNTHETIC,
500+
packageName.replace(".", "/") + "/" + generatorClassName,
501+
null,
502+
mojo,
503+
null);
504+
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_SYNTHETIC, "<init>", "()V", null, null);
505+
mv.visitCode();
506+
mv.visitVarInsn(Opcodes.ALOAD, 0);
507+
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, mojo, "<init>", "()V", false);
508+
mv.visitInsn(Opcodes.RETURN);
509+
mv.visitMaxs(-1, -1);
510+
mv.visitEnd();
511+
cw.visitEnd();
512+
return cw.toByteArray();
513+
}
514+
370515
private PluginDescriptor extendPluginDescriptor(PluginToolsRequest request) {
371516
ExtendedPluginDescriptor extendedPluginDescriptor = new ExtendedPluginDescriptor(request.getPluginDescriptor());
372517
extendedPluginDescriptor.setRequiredJavaVersion(getRequiredJavaVersion(request));

0 commit comments

Comments
 (0)