I am writing a Maven plugin and I would like to automatically resolve specific dependencies and add them as dependencies to the project based on the parameters given to the plugin.
I have been able to successfully resolve dependencies through aether, but there seems to be a disconnect between aether and the MavenProject.
There is a method on MavenProject#addAttachedArtifact which I'm guessing is what I want to use. However, it takes a org.apache.maven.artifact.Artifact while the one retrieved from aether is org.sonatype.aether.artifact.Artifact. I found a plugin that has a conversion method between the two, but I figure there ought to be a more standard approach.
I have also tried using the DefaultArtifactFactory to create a org.apache.maven.artifact.Artifact but get a NullPointerException when trying to get an ArtifactHandler.
code:
DefaultArtifactFactory factory = new DefaultArtifactFactory();
Artifact mavenArtifact = factory.createBuildArtifact("com.beust", "jcommander", "1.27", "jar");
result:
Caused by: java.lang.NullPointerException
at org.apache.maven.artifact.factory.DefaultArtifactFactory.createArtifact(DefaultArtifactFactory.java:155)
at org.apache.maven.artifact.factory.DefaultArtifactFactory.createArtifact(DefaultArtifactFactory.java:117)
at org.apache.maven.artifact.factory.DefaultArtifactFactory.createArtifact(DefaultArtifactFactory.java:111)
at org.apache.maven.artifact.factory.DefaultArtifactFactory.createBuildArtifact(DefaultArtifactFactory.java:75)
at com.foo.bar.module.IncludeModuleFrontEndMojo.execute(IncludeModuleFrontEndMojo.java:165)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:101)
... 20 more
So really, these are the things I've tried, a resolution to these issues would be great, but I'm really after the right way to do this. Any ideas?
UPDATE
I wrote my own conversion method between the two classes:
private static org.apache.maven.artifact.Artifact aetherToMavenArtifactBasic(Artifact artifact, String scope, ArtifactHandler artifactHandler) {
DefaultArtifact mavenArtifact = new DefaultArtifact(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), scope, artifact.getExtension(), artifact.getClassifier(), artifactHandler);
mavenArtifact.setFile(artifact.getFile());
mavenArtifact.setResolved(true);
return mavenArtifact;
}
and found that the MavenProject#addAttachedArtifact method is to attach an artifact to an existing artifact (i.e. attach sources/javadocs jars to an artifact), which is not my goal. Instead I got the artifacts from the maven project and add my artifact:
project.getArtifacts().add(mavenArtifact);
which adds my artifact to the project (my artifact is then shown when I call the project's getArtifactMap() and getCompileClasspathElements(). However, this change does not persist. This is the problem I was really worried about. So the question has evolved into:
Can I make changes to the MavenProject and have it persist?
I don't think this is possible and for my purposes I decided instead to require the user to add the dependency in the project's pom file (and error out if they don't have it).
It seems to be by design that you don't allow the user to muck with the project configuration through a plugin to a point where you could break the build. I found a good post on advanced MOJO development here. A quote from it:
If this parameter could be specified separately from the main
dependencies section, users could easily break their builds –
particularly if the mojo in question compiled project source code. In
this case, direct configuration could result in a dependency being
present for compilation, but being unavailable for testing. Therefore,
the #readonly annotation functions to force users to configure the
POM, rather than configuring a specific plugin only.
Related
I'm working on a SpotBugs plugin (to add extra rules to catch more bugs), and I would like to provide some optional checks that consumers can decide to include or not. Ideally when people add this plugin to their SpotBugs configuration (in their pom.xml file for example), they can add a variable specifying if they want a "full scan", or just a "basic" one.
The idea is somewhat similar to the already existing "effort" configuration, so if there's a way to read that already existing configuration inside my own SpotBugs plugin, that would work too, but I couldn't find one.
The fact that the plugin is not running directly, but it runs as part of SpotBugs makes it more tricky to actually try to define properties directly. And I tried to find that effort but I wasn't successful.
(And most of the information out there is from the point of view of the final consumer and not as a plugin developer)
Problem
In java, I have a a "Util project" using another "Mock project" when doing unit test.
My problem is that the "Mock Project" is as well using the "Util project" to build some of the Mock object.
When i use maven to build my projects, i can't build it cause one project miss the jar from the second and reverse case for the other project.
Example
As you can see in the example below, it make sense that both project needs each other and each piece of code is located in the right project, what is "Mock" is in "Mock" project, what is "Util" is in "Util" project.
public class TestProjectUtil
{
#Test myMethod()
{
//some code
GeneratedEntity obj = ProjectMockUtil.generateEntity();
}
}
public class ProjectMockUtil
{
public static EntityObj generateEntity()
{
//Some code
EntityObj obj = new EntityObj();
MethodList names = ProjectUtil.Reflection.getMethodList(obj);
//Some code
}
}
Question
How should you deal with this type of situation. I tried to force maven to still build my project and ignore failure but as soon as one class fail to compile then the generated jar does not include any class at all, so the jar is empty.
As well i do not believe that a refactoring is ultimately needed in my case, the different classes are in the right projects and i do not want to duplicate my code for the sake of having the same class in both project to satisfy maven and make it work.
What might be the best approach ?
option 1
Another way to do it is to build the first project as a JAR WITHOUT MAVEN, in this case your jar is usable by the second project when you run maven for the first time. (the JAR will need to be added as dependency in the POM).
After that you can build the first project normally with maven, then rebuild the second project again but this time change the JAR reference in the POM to use dependency from local repository (the one you just build with first project).
And for future build always use MAVEN as before, it will work properly.
Option 2
Another way to do so is to merge the 2 projects together, its not always logic to do so but in my case, it could be logic to merge 2 class util together and create a separation via package name, for instance first project under dev.helper.helperjse, then the second project dev.helper.helpermock
In this case we don't have the issue with circular reference since within a project circular reference are accepted and normal.
Option 3
Another way is to change the argument of the maven compilation plugin and pass argument to force compilation error .class to be added to the jar file and to not fail on error. (this one i did not find what are the arguments yet, Happy if someone knows).
I am trying to understand a package of java code (let's call it mainPackage), which has an import from another package(Let's call this commonUtility). I can see the import statement, but cannot see the dependency directly in the pom file. I just need to understand this to make a few changes in the commonUtility so that it can be reflected in my mainPackage jar.
for example I can see a import statement in the mainPackage class file
import com.training.tdw.commonUtility.transform;
So I am expecting to see
<dependency>
<groupId>com.training.tdw</groupId>
<artifactId>commonUtility</artifactId>
<version>1.0.1</version>
</dependency>
But it is not present. So how will i find out the ink between the two packages. Correct me if my understanding is wrong.
It seems at least one of dependencies defined in your pom.xml depends on com.training.tdw.commonUtility:1.0.1
So, you do not need to specify com.training.tdw.commonUtility:1.0.1 dependency explicitly in your pom.xml. That is an essence of "Transitive dependencies" feature appeared since Maven 2.
Transitive dependencies are a new feature in Maven 2.0. This allows
you to avoid needing to discover and specify the libraries that your
own dependencies require, and including them automatically.
See: https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html
Please note that "test" scope is not transitive!
You can use the dependency:tree goal to find it. Try running
mvn dependency:tree -Dverbose -Dincludes=com.training.*
This assumes that the group id contains com.training, its possible however that its named something silly, you might just run a full dependency tree by removing the -Dincludes and manually looking.
Alternatively if your using an IDE you could navigate to a class inside that package and there should be an option to show it in your project/package explorer. From there you could determine the JAR file name and that should correspond to the maven package artifact id
A jar can have an arbitrary GAV (groupId, artifactId, version), and it need not be connected to the package you import. Sure, many people start their package name by the groupId, but others don't so in general there is not direct way to infere the necessary (or used) jar from a given package.
If you are using Nexus 2 (or MavenCentral), there is a feature named "Classname search" which allows you to search for a (qualified) class name and get as a result the jars that contain such a class. Unfortunately, Nexus 3 has dropped this.
Introduction
I am writing a custom plugin for Gradle to allow users to publish artifacts generated by their builds to a corporate Maven repository. I want users to be able to use the standard maven-publish plugin to upload their artifacts, but without them knowing the repository's details, i.e. authentication.
Users may run local builds but they cannot publish anything to the repository. The same user-provided build scripts, when running on a build server, will be able to publish. Users may download the plugin and use it locally, but it won't work. It will either fail or do a mock no-op upload to local/dummy repo/whatever, this is not important right now. When running on the build server, the plugin will just "know it" and work properly.
As I said, I want users to use the standard maven-publish plugin. I would like something like this for the users' build.gradle:
repositories {
mavenCentral()
Organization.mavenCorporate() /* this would be it */
}
(The Organization part is not required, it's just how I have seen it works with Gradle's project extensions, which seem to be currently favored over conventions).
Current status
So far I have been able to get the above piece of the script working to some extent. When I run a build which uses my plugin, it does:
not fail at syntax check/compile time, so I know Gradle understands this piece of code.
print some info when the plugin is applied, so I know Gradle is really using it.
print my (currently hard-coded) repository's name when I do println Organization.mavenCorporate().name inside the build script, so I know the extension is working.
print my repository's name when I do println project.repositories[0].name inside the build script, so I know the project knows the repository. The name is the same as the previous print and contains a System.nanoTime() to be sure it is not being generated twice. Also, if I print the default object's toString() instead of its name, both identifiers are equal.
However, when the build tries to resolve actual dependencies, it fails with the following message:
Execution failed for task ':myproject:compileJava'.
> Could not resolve all dependencies for configuration ':myproject:compileClasspath'.
> Cannot resolve external dependency org.apache.logging.log4j:log4j-api:2.3 because no repositories are defined.
Required by:
project :myproject
> Cannot resolve external dependency org.apache.logging.log4j:log4j-web:2.3 because no repositories are defined.
Required by:
project :myproject
[...]
What bothers me is the 'because no repositories are defined' part. I would expect it to fail for a lot of different reasons, but not this one. When I add another repository which does not contain the needed dependencies, it fails with another kind of error.
The code
My plugin is written in Java. I have seen a plugin from the Gradle Plugin Portal which has a very similar purpose, but it's written in Groovy. It simply adds some methods to project.repositories.metaClass. I don't know Groovy, but that looks like a quick-and-dirty solution to avoid dealing with Gradle's API in a proper way.
As of now, I have the following:
The main plugin class:
public class MyPlugin implements Plugin<Project> {
#Override
public void apply(Project project) {
_printVersionInfo(project);
// this allows the use of Organization methods as Gradle DSL
project.getExtensions().create("Organization", Organization.class, project);
// this should add the repo to project's RepositoryManager
project.getRepositories().add(CustomRepository.getInstance(project));
}
[unrelated methods omitted]
}
The custom repository class:
public class CustomRepository implements MavenArtifactRepository {
private static MavenArtifactRepository repo;
public static MavenArtifactRepository getInstance(Project project) {
if (repo == null) {
DefaultRepositoryHandler drh = (DefaultRepositoryHandler) project.getRepositories();
repo = drh.maven(new CustomRepoAction());
}
return repo;
}
[implemented methods from MavenArtifactRepository omitted for brevity; for debugging purposes, every method except getName() and getUrl() currently throws an UnsupportedOperationException]
}
The extension Organization class. Contains only the mavenCorporate() DSL method, which returns a MavenArtifactRepository obtained with the same call to CustomRepository.getInstance(project) as in the main plugin class.
The CustomRepoActionclass which implements Action<MavenArtifactRepository>. Contains only the implementation of execute method, which sets the name and URL of the MavenArtifactRepository passed as the parameter.
I am using Java 8 and Gradle 3.4. I am relatively new to Gradle as the user, and not really familiar with the new features from Java 7 and 8.
The question
Am I missing something? Why doesn't Gradle recognize the MavenRepositoryArtifact returned by Organization.mavenCorporate() as a repository? Do I need to bind the repository with the project, the repository handler, or any kind of manager object that I am not aware of?
EDIT: Thanks to #lukegv's comment, now I realize I cannot force my custom DSL into DefaultRepositoryHandler. I tried something like this:
repositories {
mavenCentral()
maven Organization.mavenCorporateAction() /* instance of CustomRepoAction */
}
and it works, adds a MavenArtifactRepository to project's repositories. But that doesn't keep users from accessing to the repository's internals, i.e. password. However, RepositoryHandler.maven(Action<? super MavenArtifactRepository> action) doesn't allow me to add a subclass of MavenArtifactRepository in which I could control this behaviour.
Do I need to implement a RepositoryHandler and make it the default repository handler for projects? Is that even possible without implementing my own custom Project class and messing with Gradle's internals? Is there a clean way of achieving this:
I want users to be able to use the standard maven-publish plugin to
upload their artifacts, but without them knowing the repository's
details, i.e. authentication.
with decoration, or anything implying minimal or no changes to the default behavior of everything else?
Right now i am trying to do some POC with JBoss Drool Workbench.
I am facing one Problem: I have uploaded on JAR file as a dependency. which contains Bean and some of the business logic service class. which also contains class which have list of static method which need to call from Rule.
My Problem is i am able to get all beans and constant classes in Guided Rule UI in Config tab for Imports. but other then all remaining class, like class contains static method are not available for import. Due to that i am not able to import that class, and not able to use that one.
Please help me out.
Genius Thanks in Advance.
After seen warning i came to know that i also need to upload dependent jar file in workbench and also provide dependency to project, drool itself provide a hint for same. finally the problem has been resolved.
In the project setting tab, you have to add dependency of the maven project containing POJO. It will give you option to either load from central or load from local repository. If you choose, central, the pom.xml will contain the reference to download the dependency while createing the jar. If you choose from local, it will add the class files to your jar.