how to pass maven module classes to a maven plugin - maven

I've developed a maven plugin, which can scan classes within a module, to find some specific classes and do something about them.
The problem is that, when I'm using this plugin in a maven module, It's not able to find classes within that module.
I've checked the plugin classpath and it only contains plugin classes and it's dependencies.
Is there any way to automatically include module classes into plugin classpath?

I took this approach and apparently it's working:
1 - a MavenProject parameter is needed within your Mojo class:
#Parameter(defaultValue = "${project}", required = true, readonly = true)
private MavenProject project;
2 - and then you can get the classpath elements from project instance:
try {
Set<URL> urls = new HashSet<>();
List<String> elements = project.getTestClasspathElements();
//getRuntimeClasspathElements()
//getCompileClasspathElements()
//getSystemClasspathElements()
for (String element : elements) {
urls.add(new File(element).toURI().toURL());
}
ClassLoader contextClassLoader = URLClassLoader.newInstance(
urls.toArray(new URL[0]),
Thread.currentThread().getContextClassLoader());
Thread.currentThread().setContextClassLoader(contextClassLoader);
} catch (DependencyResolutionRequiredException e) {
throw new RuntimeException(e);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}

Related

Find dependencies of a Maven Dependency object

I'm writing a Maven 3 plugin that needs to know the transitive dependencies of a given org.apache.maven.model.Dependency. How can I do that?
In Maven 3, you access all dependencies in a tree-based form by relying on the maven-dependency-tree shared component:
A tree-based API for resolution of Maven project dependencies.
This component introduces the DependencyGraphBuilder that can build the dependency tree for a given Maven project. You can also filter artifacts with a ArtifactFilter, that has a couple of built-in implementations to filter by groupId, artifactId (IncludesArtifactFilter and ExcludesArtifactFilter), scope (ScopeArtifactFilter), etc. If the fiter is null, all dependencies are kept.
In your case, since you target a specific artifact, you could add a IncludesArtifactFilter with the pattern groupId:artifactId of your artifact. A sample code would be:
#Mojo(name = "foo")
public class MyMojo extends AbstractMojo {
#Parameter(defaultValue = "${project}", readonly = true, required = true)
private MavenProject project;
#Parameter(defaultValue = "${session}", readonly = true, required = true)
private MavenSession session;
#Component(hint = "default")
private DependencyGraphBuilder dependencyGraphBuilder;
public void execute() throws MojoExecutionException, MojoFailureException {
ArtifactFilter artifactFilter = new IncludesArtifactFilter(Arrays.asList("groupId:artifactId"));
ProjectBuildingRequest buildingRequest = new DefaultProjectBuildingRequest(session.getProjectBuildingRequest());
buildingRequest.setProject(project);
try {
DependencyNode rootNode = dependencyGraphBuilder.buildDependencyGraph(buildingRequest, artifactFilter);
CollectingDependencyNodeVisitor visitor = new CollectingDependencyNodeVisitor();
rootNode.accept(visitor);
for (DependencyNode node : visitor.getNodes()) {
System.out.println(node.toNodeString());
}
} catch (DependencyGraphBuilderException e) {
throw new MojoExecutionException("Couldn't build dependency graph", e);
}
}
}
This gives access to the root node of the dependency tree, which is the current project. From that node, you can access all chidren by calling the getChildren() method. So if you want to list all dependencies, you can traverse that graph recursively. This component does provide a facility for doing that with the CollectingDependencyNodeVisitor. It will collect all dependencies into a List to easily loop through it.
For the Maven plugin, the following dependency is therefore necessary:
<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-dependency-tree</artifactId>
<version>3.0</version>
</dependency>
So the following code should give you an impression how to do it.
#Mojo( name = "test", requiresDependencyResolution = ResolutionScope.COMPILE, defaultPhase = LifecyclePhase.PACKAGE ...)
public class TestMojo
extends AbstractMojo
{
#Parameter( defaultValue = "${project}", readonly = true )
private MavenProject project;
public void execute()
throws MojoExecutionException, MojoFailureException
{
List<Dependency> dependencies = project.getDependencies();
for ( Dependency dependency : dependencies )
{
getLog().info( "Dependency: " + getId(dependency) );
}
Set<Artifact> artifacts = project.getArtifacts();
for ( Artifact artifact : artifacts )
{
getLog().info( "Artifact: " + artifact.getId() );
}
}
private String getId(Dependency dep) {
StringBuilder sb = new StringBuilder();
sb.append( dep.getGroupId() );
sb.append( ':' );
sb.append( dep.getArtifactId() );
sb.append( ':' );
sb.append( dep.getVersion() );
return sb.toString();
}
}
The above code will give you the resolved artifacts as well as dependencies. You need to make a difference between the dependencies (in this case the project dependencies without transitive and the artifacts which are the solved artifacts incl. transitive.).
Most important is requiresDependencyResolution = ResolutionScope.COMPILE otherwise you will get null for getArtifacts().
The suggestion by Tunaki will work for any kind of artifact which is not part of your project...The question is what you really need?

Get gradle.properties file from Gradle custom plugin

I'm developing a Gradle plugin and I need to read and write to gradle.properties project file. I have tried this:
#TaskAction
public void myAction() {
Properties properties = new Properties();
try {
properties.load(new FileInputStream(getProject().file("gradle.properties")));
} catch (IOException e) {
e.printStackTrace();
}
}
But I get FileNotFoundException. How can I get the file?
I found the error. Gradle API Documentation says that .file(Object path) method in Project class:
Resolves a file path relative to the project directory of this
project.
My gradle.properties file was in root project, instead of in the module where plugin is applied.

How to configure a DSL style gradle plugin from a plugin extension

I have implemented a Gradle plugin using the Gradle DSL style. The plugin is adding multiple aspects such as a adding a custom task, and configuring more other tasks. Overall, the plugin is generating some metadata property file in a source folder that must be configured by a plugin extension.
apply plugin: 'artifactMetadata'
// configure the path for the metadata
artifactMetadata {
destinationDirectory = "src/main/generated/otherlocation/resources"
}
I have been able to figure out how to configure the task using the extension properties, however it's tricking me with the remaining stuff. What is a good approach to configure the source set, the clean task and the idea plugin (see the #n: TODO comments in the plugin code below)? The implementation below will always use the default value, not the one injected through the plugin extension.
class ArtifactMetadataPlugin implements Plugin<Project> {
public static final String EXTENSION_NAME = 'artifactMetadata'
public static final String TASK_NAME = 'generateArtifactMetadata'
void apply(Project project) {
createExtension(project)
project.configure (project) {
task (TASK_NAME, type: GenerateArtifactMetadata) {
group = project.group
artifact = project.name
version = project.version.toString()
}
sourceSets {
main {
// #1:TODO to get the plugin extension property current value here output.dir(project.artifactMetadata.destinationDirectory, builtBy: TASK_NAME)
resources.srcDirs += file(project.artifactMetadata.destinationDirectory)
}
}
clean {
// #2:TODO get the plugin extension property here
delete file(project.artifactMetadata.destinationDirectory)
}
if (project.plugins.hasPlugin(IdeaPlugin)) {
idea {
module {
// #3:TODO get the plugin extension property here
sourceDirs += file(project.artifactMetadata.destinationDirectory)
}
}
}
}
project.afterEvaluate {
def extension = project.extensions.findByName(EXTENSION_NAME)
project.tasks.withType(GenerateArtifactMetadata).all { task ->
task.destinationDirectory = project.file(extension.destinationDirectory)
}
}
}
private static void createExtension(Project project) {
def extension = project.extensions.create(EXTENSION_NAME, ArtifactMetadataPluginExtension)
extension.with {
destinationDirectory = "src/main/generated/artifactinfo/resources"
}
}
}

ResourceLoader is throwing exception

I have a code like follows
public LocalFileStorage(String storageUrl, Resource storageDirectory) {
this.storageUrl = storageUrl;
try {
this.storageDirectory = storageDirectory.getFile();
this.storageDirectory.deleteOnExit();
this.storageDirectory.createNewFile();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
I call the class the follows.
private ResourceLoader resourceLoader; // from spring
LocalFileStorage pictureStorage = new LocalFileStorage(Url+ "/resources/", resourceLoader.getResource("/resources/"));
call to
resourceLoader.getResource("/resources/")
throws exception. I thought ResourceLoader loads directory also because after all directory is also a file.
My structure
Typically, only anything inside /WEB-INF/classes, /WEB-INF/lib, /WEB-INF/... will be added to the classpath and accessible through ClassLoader.getResource(). The folder you are trying to access is not in WEB-INF and will therefore not appear in the classpath.
Assuming you are using something similar to Maven, you should put resource files under /src/main/resources. When your project is built, those files will end up in WEB-INF/classes.

How to override your Propertyfile for jUnit (maven)

I have a Propertyfile config.properties in which I store a Path which a class loads to read Files.
The properties are loaded like this:
public class PropertyConfig {
private static final Properties properties = new Properties();
static {
try {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("config.properties"));
} catch (IOException e) {
throw new ExceptionInInitializerError(e);
}
}
public static String getSetting(String key) {
return properties.getProperty(key);
}
}
and the call in the relevant class is like this:
private static File savedGamesFolder = new File(PropertyConfig.getSetting("folder_for_saved_games"));
For testing purposes I want to be able to change the path to a test directory, or change the whole Property-file in a jUnit-TestCase. How can achieve this?
I'm using Maven if that helps.
Assuming you have your config.properties in
src/main/resources/config.properties
Note: you should nevertheless have your properties files somewhere in src/main/resources
Place your test configuration in
src/main/test/config.properties
That's it. No need to change your code.

Resources