Invocation of MavenCli fails within a Maven plugin - java

I've created small util to wrap MavenCli, which generates a new Maven project, using the quickstart archetype.
When executing the Util as a unit test, it is working quite well (just generating an empty Maven project).
Now I want to integrate this small wrapper into a Maven plugin. But when I execute the mojo (within a third Maven project), the invocation of MavenCli fails with exception:
[ERROR] Error executing Maven.
[ERROR] java.util.NoSuchElementException
role: org.apache.maven.eventspy.internal.EventSpyDispatcher
roleHint:
[ERROR] Caused by: null
The util looks like:
public void createProject() {
final MavenCli cli = new MavenCli();
System.setProperty("maven.multiModuleProjectDirectory", "/usr/share/maven");
cli.doMain(new String[] { "archetype:generate", "-DgroupId=com.my.company",
"-DartifactId=hello-world", "-DarchetypeArtifactId=maven-archetype-quickstart",
"-DinteractiveMode=false" }, "/tmp", System.out, System.out);
}
relevant dependency of the util:
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-embedder</artifactId>
<version>3.3.9</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>3.3.9</version>
</dependency>
The mojo code looks like:
#Mojo(name = "custommojo", requiresProject = false)
public class CustomMojo extends AbstractMojo {
#Override
public void execute() throws MojoExecutionException, MojoFailureException {
Util.createProject();
}
}
The POM of the mojo just includes dependencies to relevant Maven artifacts (plugin-api, plugin-annotation, etc.) and the util.
The third project I mentioned, is an empty "maven-quickstart" project which have dependency to the mojo-project and a configuration for the mojo to execute in compile phase.
I have no idea why it works in context of unit test, but not in context of a mojo.
Can anybody help?

This is a class loading issue.
MavenCli will try to load classes from the context classloader of the current thread. Inside of a Maven plugin, there is a special, restricted, classloader, which has access to:
its own classes;
the classes used in its dependencies block;
exported classes as part of a possible build extension of the project;
exported classes from the Maven core and core extensions;
and has the bootstrap classloader as parent.
However, the specific class org.apache.maven.eventspy.internal.EventSpyDispatcher is part of Maven core, but it is not part of the exported APIs (the package org.apache.maven.eventspy is not listed as an exportedPackage). So the plugin cannot load that class. This is also why it works in your tests: you're not inside of a plugin, so the classloader is different and has access to that class.
You won't even be able to get away with adding explictly a dependency on maven-core for the plugin: it will be disgarded since it is supposedly already provided.
There are 2 solutions here:
Don't use the Maven Embedder API from within a plugin, but the Invoker API. The difference between the two is that the Invoker API will launch Maven in a clean environment, completely distinct with regard to the current one. Since it starts everything anew, you won't have any classloading issue.
Use the mojo-executor library, that provides an easy way to invoke other Mojos from within a Maven plugin. You could use it here to invoke the archetype:generate Mojo.

This works for me inside a custom maven plugin (using Maven 3.5.0):
ClassRealm classRealm = (ClassRealm) Thread.currentThread().getContextClassLoader();
MavenCli cli = new MavenCli(classRealm.getWorld());
cli.doMain( ... );
The plexus Launcher sets the context class loader to its ClassRealm, which has access to the "global" ClassWorld.
Not sure how stable that solution is, but so far looking good.
Used imports:
import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.apache.maven.cli.MavenCli;

#Alexander:
You answer put me in the right direction, I was getting the mentioned error when trying to run to doMain maven commands subsequently (one command would succeed).
However your code gives me a ClassCast exception :
Exception in thread "main" java.lang.ClassCastException: sun.misc.Launcher$AppClassLoader cannot be cast to org.codehaus.plexus.classworlds.realm.ClassRealm
at com.misys.tools.integration.codegen.cli.cmd.InstallApi.mavenInstallMchApi(InstallApi.java:58)
at com.misys.tools.integration.codegen.cli.cmd.InstallApi.run(InstallApi.java:50)
at com.misys.tools.integration.codegen.cli.OpenApiGen.main(OpenApiGen.java:26)
I managed to rewrite the code to:
MavenCli cli = new MavenCli(new ClassWorld("maven",Thread.currentThread().getContextClassLoader()));
And then 2 subsequent doMain invocations of embedded maven succeed!

Related

How to configure IntelliJ and hibernate-jpamodelgen annotations to launch test class from the IDE

We have a git multimodules projects MAVEN on IntelliJ. We use hibernate-jpamodelgen for criteria builder API.
We have web project using maven dependency entities library which are generating annotated class in entities => target folder.
When launching Install and test from mvn terminal it is working like a charm, but the issue is if we want to debug a test we have to launch with
-Dmaven.surefire.debug
option and with a remote application. It takes a time and not efficient. The problem is when we try to launch with right maven configuration (in IntelliJ) or directly in the class clicking the "play button" close to the method name it does not work, because it is creating in the web project all annotated classes but with empty body
for example what is in the dependency lib
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor", date = "2022-12-30T10:11:22.168+0400")
#SuppressWarnings({ "deprecation", "rawtypes" })
#StaticMetamodel(FixedAsset.class)
public abstract class FixedAsset_ extends com.seanergie.persistence.ObjectWithUnidAndVersion_ {
public static volatile SingularAttribute<FixedAsset, LocalDate> purchaseDate;
public static volatile SingularAttribute<FixedAsset, String> serialNumber;
public static volatile SingularAttribute<FixedAsset, MutableMoney> cost;
public static volatile SingularAttribute<FixedAsset, String> notes;
public static volatile SingularAttribute<FixedAsset, FixedAssetFilesStore> filesStore;
and what is created in other dependency target annotated class folder if i try to launch from intellij
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor", date = "2022-12-30T10:12:05.141+0400")
#SuppressWarnings({ "deprecation", "rawtypes" })
#StaticMetamodel(FixedAsset.class)
public abstract class FixedAsset_ extends com.seanergie.persistence.ObjectWithUnidAndVersion_ {
}
As you can see class is empty so compilation not work
We should be able to launch directly without creating a test configuration and modifying it (we have more than 100 test classes, so we can't create a conf for each one).
We see also that it try to compile all projects on each test
we tried to add in pom.xml
<dependency><!-- For launch tests directly from IntelliJ play button-->
<groupId>org.hibernate</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
<exclusions>
<exclusion>
<groupId>com.sun.activation</groupId>
<artifactId>jakarta.activation</artifactId>
</exclusion>
</exclusions>
<scope>provided</scope>
</dependency>
We also tried to deactivate or activate annotation processor in IntelliJ settings but nothing works
here in web project the hibernate mapping
<properties>
<mainClass>com.intranet.Main</mainClass>
<datasource.uri>jdbc:postgresql://localhost:5432/intranet?charSet=utf-8&amp;ApplicationName=${DATASOURCE_APPLICATION_NAME}</datasource.uri>
<datasource.pool_size.min>15</datasource.pool_size.min>
<datasource.pool_size.max>30</datasource.pool_size.max>
<hibernate.mapping.files>
<![CDATA[
<mapping-file>com/intranet-base.entities.xml</mapping-file>
<mapping-file>com/intranet-webapp.entities.xml</mapping-file>
<mapping-file>com/intranet-intranet.entities.xml</mapping-file>
<mapping-file>com/intranet-webapp.entities.xml</mapping-file>
<mapping-file>com/intranet2-intranet.entities.xml</mapping-file>
]]>
</hibernate.mapping.files>
<databaseInitializationHook.class>com.intranet.persistence.initialization.DatabaseInitializationHook</databaseInitializationHook.class>
<test.databaseInitializationHook.class>com.intranet.persistence.initialization.DatabaseInitializationHook</test.databaseInitializationHook.class>
</properties>
So finally when we launch mvn clean install even from IntelliJ (not only terminal) it create all annotated classes correctly in each library where we defined entities, but when we launch test it create another time same classes, but with empty body and in projects where entities are not defined (but use others as dependencies !)
What is the good way to make it work?
I do believe I have solved your puzzle.
The appearance of "empty" metamodel classes could be caused by one of the following:
some of plenty IDEA plugins influences on compile process and, obviously, fails
jpamodelgen annotation processor may perform extra work
And the last reason seems to be the actual one:
persistenceXml:
Per default the processor looks in /META-INF for persistence.xml. Specifying this option a persitence.xml file from a different location can be specified (has to be on the classpath)
ormXml:
Allows to specify additional entity mapping files. The specified value for this option is a comma separated string of mapping file names. Even when this option is specified /META-INF/orm.xml is implicit.
fullyAnnotationConfigured:
If set to true the processor will ignore orm.xml and persistence.xml.
I have checked and indeed placing META-INF/orm.xml with defined "foreign" entities into resources folder causes jpamodelgen to generate empty metamodel classes - as for me it looks like a bug.
You have following options (actually, depends on your project structure):
remove jpamodelgen from module dependencies if module does not define entity classes, also make sure dependency on jpamodelgen is defined as non-transitive, i.e. <scope>provided</scope>
rename extra xml mapping files - if you are on spring it is possible to specify those mappings via spring.jpa.mapping-resources
configure annotation processor as described in JBoss guide
UPD. based on #cyril comment.
First of all, defining dependencies (not imposing versions via <dependencyManagement>) in parent pom is a terrible idea - you have no chance to exclude such dependency in child modules, so, your parent pom is definitely broken.
However, I do believe you can meet your objectives (i.e. running tests via "green play button") even with broken parent pom - just follow JBoss guide and configure maven-compiler-plugin like:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgs>
<arg>-AfullyAnnotationConfigured=true</arg>
</compilerArgs>
</configuration>
</plugin>
IntelliJ does recognise such configuration.

Unable to integration-testing gradle multi-module Spring-Boot-Application

Within a Gradle multi-module project with the bootstrapping in its own module I'm unable to use MockMvc, because its need to reference the bootstrapping-module. I'm not sure if I have misconfigured something. The basic structure is:
module: a module containing some REST-Services and needs a testImplementation-Dependency on starter
starter: the bootstrapping-module which gets the spring-boot-plugin applied and depends on module
I have set up a minimal example on github using Spring-Boot 2.3.1.RELEASE and Gradle 6.4 with the following configuration:
./settings.gradle.kts
rootProject.name = "spring-multimodule-integrationtest"
include("starter", "module")
./build.gradle.kts
subprojects {
repositories {
jcenter()
}
dependencies {
apply(plugin = "java-library")
"testImplementation"("junit:junit:4.12")
}
}
./starter/build.gradle.kts
plugins {
id("org.springframework.boot") version "2.3.1.RELEASE"
}
dependencies {
implementation(project(":module"))
}
./module/build.gradle.kts
dependencies {
testImplementation(project(":starter"))
}
The starter-module contains only one a single class "Starter" referencing the module-module:
public class Starter {
public String info() { return "starter"; }
public static void main(String[] args) {
System.out.println(new Starter().info() + " and " + new Module().info());
}
}
The module-module (*sigh I should have chosen a different name for this module) contains only this implemenation-class:
public class Module {
public String info() { return "module"; }
}
Additionally, the module-module has the following test-class doing the integration-test:
public class IntegrationTest
{
#Test public void testSomeLibraryMethod() {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
System.setOut(new PrintStream(out));
Starter.main(new String[0]);
assertEquals("starter and module\n", out.toString());
}
}
This code runs fine until the applying of the spring-boot-plugin within "./starter/build.gradle.kts". When the tasks "clean test" issued on the shell I get:
❯ ./gradlew clean test
> Task :module:test FAILED
de.kramhal.multi.IntegrationTest > testSomeLibraryMethod FAILED
java.lang.NoClassDefFoundError at IntegrationTest.java:17
Caused by: java.lang.ClassNotFoundException at IntegrationTest.java:17
1 test completed, 1 failed
This problem does not occur, when tests are executed within the IDE (IntelliJ to be exact).
I already tried unsuccessfully to use the spring-dependency-management as suggested in this answer (as well as in several other answers).
What have I done wrong?
First off, I would recommend restructuring your project so you don't have cyclic dependencies. As it is now, in order to build starter, you need to build module. And in order to test module, you need to build starter. Gradle can do it, but it is usually a smell.
In terms of troubleshooting: when you get a test failure like this, look at the test report as that has the full stack trace. You should see that it complains that it can't find the Starter class (Caused by: java.lang.ClassNotFoundException: de.kramhal.multi.Starter), which is of cause in the starter module.
You mentioned the spring-dependency-management plugin, but that is only relevant for managing Maven dependencies, and not project dependencies like this. So it is not helpful here.
I am not entirely sure if this is Windows specific or not as I remember there were some discussions around performance a while back when having a lot of classes. But I believe the java-library plugin will look for jar files in other projects, and not the folder for compiled classes. This is a problem for you since the spring-boot plugin will by default disable the standard jar task and instead create "fat" a jar file through the bootJar task. Because you need both the fat jar for packaging the application to run stand-alone but also the normal jar for consuming it as a dependency, you need to do some tweaks to the starter project (Kotlin DSL):
tasks {
jar {
enabled = true
}
bootJar {
archiveClassifier.set("boot")
}
}
This will enable the normal jar file, but because the name will conflict with the one produced by the bootJar task, you need to rename one of them. I chose to rename the bootJar one.
I don't know why the test works for you in IntelliJ as that should, by default, delegate everything to Gradle. But maybe you have an old version, or done some manual configuration to let IntelliJ both compile and run your tests.

How do I export SQLJDBC into JAR correctly in Intellij?

I'm setting up a SQL connector for my bukkit plugin, but whenever I compile into a jar file and try running from the server I get
java.sql.SQLEXCEPTION: No suitable driver found.
I have tried adding in the line
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
Whenever I put that I get
SQLEXCEPTION: com.microsoft.sqlserver.jdbc.SQLServerDriver
This problem only occurs when I put it into a jar file and not when I test it in my IDE.
I currently use Intellij.
This is my current Jar setup:
https://gyazo.com/94341b7bb47121a0416deaee6279dd30
public ConnectionUtils(String url, String us, String pa) throws SQLException
{
SQLServerDriver dr = new SQLServerDriver();
user = us;
pass = pa;
con = DriverManager.getConnection(url, us, pa);
isConnected = true;
this.url = url;
}
Solution using Project Structure
Outside of using maven, you can look at your Project Structure in IntelliJ and examine your dependencies. Make sure the "export" box next that jar is checked.
Maven Solution
I recommend using maven to handle your dependencies, as you can define the scope of the dependency, as explained here.
For the JDBC dependency, you could use the following dependency declaration in your pom:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
This would inherit the default scope which, as stated in the article, is compile.
3.1. Compile
This is the default scope when no other scope is provided.
Dependencies with this scope are available on the classpath of the
project in all build tasks and they're propagated to the dependent
projects.
Manifest
It seems some people online are talking about exactly this issue, and the solution is one that #Bohemian mentioned for ensuring that the required class is packaged with the jar. However, that solution only works if you are executing the jar from the command line, which is not the case with spigot plugins. I suggest creating a MANIFEST.txt and including the driver class-path in there, as suggested by Terence Gronowski on CodeRanch
Creating a Manifest.txt file with the following contents in the
program folder:
Manifest-Version: 1.0 Class-Path: sqljdbc4.jar Main-Class:
ParkplatzVerwaltung (Newline)
Don't forget to end with a newline. The important thing is
"Class-Path: sqljdbc4.jar", showing where the driver is.
Source: https://coderanch.com/t/529484/databases/Jdbc-driver-putting-application-jar

Third party API can't see class in multi module Maven project

I have a multi module maven project, and in the dao module, I added the JSON-IO dependency. When I try to deserialize my object, it gives me:
Exception in thread "main" com.cedarsoftware.util.io.JsonIoException: Class listed in #type [hu.kleatech.projekt.model.Employee] is not found
The class name is correct, the Employee is public, and the dao has the module as dependency. What could have gone wrong?
Edit: Since this is an old question and have been answered long ago, I'm deleting the github repository that I made specifically for this question. The solution to the problem is in the accepted answer, the exact code is not relevant.
Please try adding an empty constructor to Employee class.
Edit: Actually, while adding an empty constructor solves the problem, it is not necessarily required. Json-IO "will make a valiant effort to instantiate passed in Class, including calling all of its constructors until successful. The order they tried are public with the fewest arguments first to private with the most arguments."
(copied from MetaUtils.java javadoc)
Also, when calling a multi-argument constructor, the library fills the arguments with nulls and defaults for primitives. Then any exceptions thrown during the constructor call is ignored. In your case, a NullPointerException was thrown, because the constructor is not null-safe. So either modify the constructor so that it can handle nulls, or add an empty constructor.
Maven dependency configuration is hierarchical from <parent> element not from <modules> element.
It means that in the project's pom.xml file where you have dependency on "JSON-IO dependency" you do not have dependency on your dao project or where that class is.
<modules> stands only to define what projects to build. Order of modules definition does not matter, since Maven detects order by required dependencies
So, you can define dependency in <parent> pom.xml either in
<dependencies> element. then all children will have it.
or in <dependencyManagement> - then children who need it can include it in their <dependencies> without common configurations like version, scope etc...
look at quite similar answer here:
How to minimize maven pom.xml
As per your project and modules Pom your main Pom should have modules in following order ....
<modules>
<module>core</module>
<module>controller</module>
<module>service</module>
<module>dao</module>
</modules>
service depends on core so core should be build before service
dao depends on service and core both so dao should be after core and service.
Employee class is available in core and it should be available in core jar.
You should add depencyManagent in main Pom and then add all the module as dependencies in dependencyManagement so whoever adds your main Pom as dependency will be able to access all your jars.
Once you change order build your project again and then update your maven project.
If this code is being used in another project then make sure that you have uploaded jars to repository (mvn deploy) so whoever uses it can download it when they are building their project.
One way to verify whether this jar is downloaded in the main project where it is used or not is check in project explorer there would be a Maven Dependencies section where you can see all dependency jars and check if core is present or not.
I am not sure what controller module is doing in main Pom as I couldn’t find a module by that name in your project so you should either remove it or add a module (folder) for it.

NoSuchMethodFound error: dependency error in GWTP. ResourceDelegate usage

I'm working in a GWTP + GAE project based on the CarStore example delviered by ArcBees. Maven dependencies are taken from that project too. My current version of GWTP is 1.5-SNAPSHOT.
The project compiles perfectly well. But when I run it, I'm stucked in this error:
[ERROR] Error injecting com.gwtplatform.dispatch.rest.delegates.client.ResourceDelegate<com.rmideas.sportbinder.shared.api.SessionResource>:
Unable to create or inherit binding: No #Inject or default constructor found for com.gwtplatform.dispatch.rest.delegates.client.ResourceDelegate<com.rmideas.sportbinder.shared.api.SessionResource>
The error refers to the ResourceDelegate declared in LoginPresenter. But i can´t find anything wrong in my code.
I analyzed the stacktrace and found this cause:
[INFO] Caused by: java.lang.NoSuchMethodError: com.gwtplatform.dispatch.rest.rebind.events.RegisterGinBindingEvent.postSingleton(Lcom/google/common/eventbus/EventBus;Lcom/gwtplatform/dispatch/rest/rebind/utils/ClassDefinition;Lcom/gwtplatform/dispatch/rest/rebind/utils/ClassDefinition;)
[INFO] at com.gwtplatform.dispatch.rest.delegates.rebind.DelegateGenerator.maybeRegisterGinBinding(DelegateGenerator.java:162)
This refers to a call between two Class objects. DelegateGenerator is the caller and is obtained from this dependency:
<dependency>
<groupId>com.gwtplatform.extensions</groupId>
<artifactId>dispatch-rest-delegates</artifactId>
<version>${gwtp.version}</version>
<scope>provided</scope>
</dependency>
And RegisterGinBindingEvent is the receiver. It belongs to here:
<dependency>
<groupId>com.gwtplatform</groupId>
<artifactId>gwtp-dispatch-rest</artifactId>
<version>${gwtp.version}</version>
<scope>provided</scope>
</dependency>
Now, this happens in line 162 of DelegateGenerator:
RegisterGinBindingEvent.postSingleton(eventBus, definition, getClassDefinition());
This shouldn't be a problem, but the key the eventBus sends as the first parameter is of a different type in both classes.
DelegateGenerator has this import statement:
import com.google.common.eventbus.EventBus;
And RegisterGinBindingEvent has this EventBus class:
import com.gwtplatform.dispatch.rest.rebind.utils.EventBus;
I guess that this is causing the problem. Both dependencies are using the same version (1.5-SNAPSHOT). If I downgrade the version to 1.4, this won´t happen. But it still has issues running ResourceDelegate with this code.
Does anyone know what am I be doing wrong? Or if this is a bug?
Thanks for using the snapshots.
For some reason the CI didn't deploy a snapshot for the last commits. This should now be fixed. Can you try another build? Add -U to your maven command line to make sure you grab the latest snapshot.

Categories