How can I programmatically create a Plexus container for Maven? - maven

I've written a part of a Maven plugin, but it can no longer run as a plugin. It have access to SCM, or any pom.xml, just an artifact repository and some Maven coordinates. It is meant to take Maven coordinates and print out their dependencies (performing some reporting on them). To do this it needs programmatic access to (for example):
MavenProjectBuilder
DependencyGraphBuilder
The usual stuff you'd injected into a mojo, but it's not a mojo and cannot be run as such (as I don't have a pom.xml).
The code I have hasn't helped me, e.g. AbstractMojoTestCase requires a pom.xml, something I don't have.
I'd like some code like..
DefaultPlexusContainer container = new DefaultPlexusContainer();
Context context = container.getContext();
context.put("plexus", new DefaultPlexusContainer());
container.initialize();
container.start();
MavenProjectBuilder mavenProjectBuilder = (MavenProjectBuilder) container.lookup(MavenProjectBuilder.ROLE);
DependencyGraphBuilder dependencyGraphBuilder = (DependencyGraphBuilder) container.lookup(DependencyGraphBuilder.class.getName());
MavenProject project = mavenProjectBuilder.buildFromRepository(artifact, remoteArtifactRepositories, localRepository);
// do stuff with dependencies
But there's something missing. Help!

Have a look at how it's done https://github.com/jenkinsci/lib-jenkins-maven-embedder (we use that in jenkins for similar purpose)

Related

Gradle Custom String Notation in dependency

I'm creating a global repository for all the artifacts that I'm producing. Right now I've created an S3 bucket as repository where I'm storing the artifacts.
As there is no group (artifacts are global), I ended up duplicating the name as the group so the current notation for a dependency is Name:Name:Version. For this, I've created an extension function that takes two arguments, a name (the dependency that I want to add to the project) and the version.
For example, I would add implementation("Name", "1.0") if I wanted the dependency Name in my project. This is translated to implementation("Name:Name:1.0") and works fine but I feel that it is a little bit ugly and can be confusing, the dependency in the External Libraries tree in IntelliJ shows the dependency as Gradle: Name:Name:1.0, the longer the name is, the uglier it is.
The question is, is it possible to write a custom Notation that let me just do implementation("Name", "1.0") without an extension function so it only shows Gradle: Name:1.0 and everything else is handled in the background?
I have looked at the class ParsedModuleStringNotation and it seems the thing that I would need to change (create my own), but the creation of the objects is hardcoded, I am unsure how to proceed from there.
if you declare S3 repository as flatDir - there is an option for what you are asking
https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.dsl.RepositoryHandler.html
flatDir(args)
Adds a resolver that looks into a number of directories for artifacts. The artifacts are expected to be located in the root of the specified directories. The resolver ignores any group/organization information specified in the dependency section of your build script. If you only use this kind of resolver you might specify your dependencies like ":junit:4.4" instead of "junit:junit:4.4".
was able to work as below
repositories {
def s = project.rootDir.toString() + "/lib/"
flatDir dirs: s
mavenCentral()
}
//compileOnly 'javax.jms:jms-api:1.1-rev-1'
compileOnly ':my-jms:1.2'

Primitive Axon App run as Fat JAR Doesn't Autoconfigure Axon Beans

PROBLEM:
RESEARCH: At https://gitlab.com/ZonZonZon/simple-axon.git I've made up a simple Axon-app to show that JAR-artifact built with Gradle-plugin com.github.johnrengelman.shadow doesn't autoconfigure Axon beans when (when run as JAR). Though it runs fine under Intellij.
From project root in terminal:
run gradle clean build shadowJar;
java -jar build/simpleaxon.jar;
Stacktrace is enclosed here. I expect that Axon Autocongiguration provides beans like CommandBus, Snapshotter and other by default.
QUESTION: How to autoconfigure default axon beans in a fat jar?
So, this took my some investigation to get a hunch what is going wrong, but I know what the problem is.
Quick notice, it's not an Axon specific thing, rather the plugin you are using.
I ran your sample project and indeed ended up with the same result; no Axon beans were being wired, ever. That led me to investigate the process of creating fat JAR's step by step. First Maven, then Spring Boot with Maven, then Gradle with Spring Boot and finally with the Shadow plugin you are referring too.
This endeavour landed me on this issue, which states as much as "projects which require the use of META-INF files need to add this to the shadow plugin, and this should be documented".
The portion referenced through this is the following:
import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer
// Left out all other specifics from your 'build.gradle' file
shadowJar {
// Required for Spring
mergeServiceFiles()
append 'META-INF/spring.handlers'
append 'META-INF/spring.schemas'
append 'META-INF/spring.tooling'
transform(PropertiesFileTransformer) {
paths = ['META-INF/spring.factories' ]
mergeStrategy = "append"
}
setArchiveFileName("simpleaxon.jar")
getDestinationDirectory().set(new File(projectDir, "./build"))
}
After adding that piece of logic to your build.gradle file, I could run your sample project as expected.
I've hit a similar issue when using Axon in a multimodule Gradle project. The app would not work when packaged and worked fine in IDE. The exact error I was getting was
org.axonframework.messaging.annotation.UnsupportedHandlerException: Unable to resolve parameter 0 in handler
The reason for this was because ParameterResolverFactories were not loaded due to the META-INF/services resources not being resolved correctly in the shadow jar plugin as #Steven hinted.
I've managed to fix it with simply (using Kotlin DSL in Gradle):
tasks.shadowJar {
mergeServiceFiles()
}
#Steven 's solution was the only one working for me, after searching for a long time for other solutions.
The Gradle Kotlin Version looks like this https://github.com/spring-projects/spring-boot/issues/1828#issuecomment-607352468:
import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer
plugins {
id("com.github.johnrengelman.shadow") version "7.1.2"
}
...
tasks.shadowJar {
// Required for Spring.
// The shadowJar plugin should merge the services correctly, but it doesn't!
mergeServiceFiles()
append("META-INF/spring.handlers")
append("META-INF/spring.schemas")
append("META-INF/spring.tooling")
transform(
PropertiesFileTransformer().apply {
paths = mutableListOf("META-INF/spring.factories")
mergeStrategy = "append"
})
}

maven mojo for reading app classes and generating java

I want to write a maven plugin which will explore the classpath of my application at build time, search for classes with a certain annotation, and generate some java code adding utilities for these classes, which should get compiled in the JAR of the application.
So I wrote a mojo, inheriting from AbstractMojo, and getting the project through:
#Parameter(defaultValue = "${project}", readonly = true, required = true)
private MavenProject project;
I have most of the code, and my mojo does get execute, but I'm having trouble inserting my mojo at right build phase.
If I plug it like that:
#Mojo(name = "generate", defaultPhase = LifecyclePhase.GENERATE_SOURCES,
requiresDependencyResolution = ResolutionScope.COMPILE)
then the java code which I generate is compiled in the JAR file.
Note that I use project.addCompileSourceRoot to register the output folder.
But that isn't enough for me because it's too early in the build: I cannot read the classpath and find the classes from my project. I think they're not compiled yet.
I search for classes like so:
final List<URL> urls = List.ofAll(project.getCompileClasspathElements())
.map(element -> Try.of(() -> new File(element).toURI().toURL()).get());
final URLClassLoader classLoader = new URLClassLoader(urls.toJavaList().toArray(new URL[0]), Thread.currentThread().getContextClassLoader());
final Set<Class<?>> entities = HashSet.ofAll(new Reflections(classLoader).getTypesAnnotatedWith(MyAnnotation.class));
(I'm using vavr but you get the gist in any case)
So, by plugging my code at the GENERATE_SOURCES phase, this code doesn't work and I don't find any classes.
However, if I plug my mojo at the PROCESS_CLASSES phase:
#Mojo(name = "generate", defaultPhase = LifecyclePhase.PROCESS_CLASSES,
requiresDependencyResolution = ResolutionScope.COMPILE)
Then my classes are found, I can access the rest of the code from the application, but the code that I generate is not taken into account in the build, despite using addCompileSourceRoot.
How do I get both features working at the same time: ability to explore code from the rest of the application and ability to generate code which will be compiled with the rest of the JAR?
I guess a possible answer would be "you can't", but as far as I can tell, querydsl and immmutables are doing it (I tried reading their source but couldn't find the relevant code).
So #khmarbaise was right, what I wanted was not a maven mojo, but rather a maven annotation processor.
I found that this walkthrough was very helpful in creating one, and also this stackoverflow answer came in handy.

Is it possible to access Maven project information from a custom plugin?

I'm writing a custom plugin in Maven and would like to access information about the project. As a simple example, within my Java code, I'd like to get the project's build directory. I know that I can get it using a parameter annotation like so:
#Mojo( name="myplugin" )
public class MyPluginMojo extends AbstractMojo {
// DOES work. The project.build.directory prop is resolved.
#Parameter( property="myplugin.buildDir", defaultValue="${project.build.directory}", required=true )
private File buildDir;
public void execute () throws MojoExecutionException
{
System.out.println(buildDir.getPath());
// DOES NOT work, prints the literal string.
System.out.println("${project.build.directory}");
}
}
That feels like a hack. To start with, I have no need to expose this parameter to the pom.xml. I'm only doing it this way because within the annotation, the property gets resolved.
I'd also like access to other properties, namely the project's dependencies.
I've been googling for hours without luck. The closest thing I've found is the MavenProject plugin but I can't get it to work either and it hasn't been updated since 2009 from the looks of it.
Gradle provides the "project" variable for this when writing plugins. Does Maven simply not allow this?
--- Update ---
Thanks to Robert's link to the documentation, I got this working. One thing that was a surprise to me was that the project.build.directory is not available through the injected project. According to the docs, you inject that separately. Here's what I added to my class to get both the project object and the build directory:
#Parameter( defaultValue="${project}", readonly=true, required=true )
MavenProject project;
#Parameter( defaultValue = "${project.build.directory}", readonly=true, required=true )
private File target;
And a dependency to my pom:
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-project</artifactId>
<version>3.0-alpha-2</version>
</dependency>
You're on the right path, it not a hack. But if you don't want to expose it as a parameter, then you should also add readonly=true. Maven also has the project variable, see http://maven.apache.org/plugin-tools/maven-plugin-tools-annotations/ for all common objects you can use within your project.
The only way to get current Maven project inside a Maven plugin is to inject it. See this question for more info.
See also the Mojo Cookbook that describe how to inject the current Maven project.

weblogic-maven-plugin WS clientgen creates SequenceCodec instead of class

I am coverting an ant project to Maven. Using the class
weblogic.wsee.tools.anttasks.ClientGenTask
Ant was generating the classes from the WSDL. Example:
com.company.ArrayOfQualifyingActivity
But now using Maven plugin, it's creating
com.company.ArrayOfQualifyingActivitySequenceCodec
Has anyone else run into this before?
Edit:
Never mind. Resolved the issue. The previous clientgen was wrapping arrays of classes (for QualifyingActivity[] ) in a wrapper class.
The maven clientgen is not doing that. So I can use the QualifyingArray[] or String[] etc... as is.
Thanks

Resources