Maven Invoker - Using MVNW - java

I use maven invoker to compile generated source code programmatically.
I work now inside container and I would like to avoid of embedding MAVEN as well.
Currently the code (that runs locally) generally looks like this (I simplified it a bit):
InvocationRequest request = new DefaultInvocationRequest();
request.setPomFile(new File("pom.xml"));
request.setGoals(Collections.singletonList("test"));
Invoker invoker = new DefaultInvoker();
invoker.setMavenHome(new File(System.getenv("M2_HOME")));
try {
invoker.execute(request);
} catch (MavenInvocationException e) {
e.printStackTrace();
}
I want to redirect the invoker to the MVNW (Maven wrapper) I have along with the code.
Anyone has an idea if this is possible or supported?
Thanks!

It's possible
Although the creators of the plugin seem not to support that aspect directly, you can configure the Maven Invoker API to run Maven Wrapper using the Invoker's methods setMavenExecutable and setMavenHome.
There are two ways to achieve this:
Using an absolute path of the Maven Wrapper script
Invoker invoker = new DefaultInvoker();
invoker.setMavenHome(Path.of(".").toFile());
invoker.setMavenExecutable(new File("/home/user/projects/sample/mvnw"));
Using a relative path of the Maven Wrapper script
Invoker invoker = new DefaultInvoker();
invoker.setMavenHome(Path.of(".").toFile());
invoker.setMavenExecutable(new File("../mvnw"));
You can set the executable without any problem if MVNW script's path is absolute.
When it's relative then the internals of the plugin will try to prepend the <M2_HOME>/bin/ path - you have to skip (../) one parent folder when declaring the executable location (so the final path seen by the plugin will look like <M2_HOME>/bin/../mvnw).
As for M2_HOME - you have to set it via the API as well because of the plugin requirements. If you won't configure it, an exception will be raised:
Maven application directory was not specified, and ${maven.home} is
not provided in the system properties. Please specify at least on of
these.
Thankfully, you can safely set it to your project directory because since Maven 3.5.0 M2_HOME env is not necessary anymore.
If your MVNW script is not placed in the main directory of the project, you have to configure the paths appropriately.
While writing this answer I was using maven-invoker:3.0.1.

Related

java.lang.ClassNotFoundException: io.quarkus.runtime.Quarkus

I am trying to run runner jar of the quarkus application which would be listening over port 9411 on http.
Programmatically using UrlClassLoader, when I try to load the jar it throws
(also with java -jar)
1.java.lang.ClassNotFoundException: io.quarkus.runtime.Quarkus
2.java.lang.reflect.InvocationTargetException
here is the snippet of code ,
URLClassLoader loader = new URLClassLoader(
new URL[]{ new File(<location of runner jar>).toURI().toURL()});
Thread.currentThread().setContextClassLoader(loader);
Class<?> mainClass = loader.loadClass("io.quarkus.runner.GeneratedMain");
Method mainMethod = mainClass.getMethod("main", String[].class);
mainMethod.invoke(null, (Object) new String[]{});
another observation is when I place /lib folder at the runner jar location it loads successfully meaning it requires the lib folder altogether.
How can I make my code work only with runner jar?
To produce a fat jar that includes all the libraries necessary to run the app, use the property quarkus.package.uber-jar=true (you can add that into src/main/resources/application.properties or pass it as a system property when running the build).
With mvn clean package I got the following error starting:
Unrecognized configuration key "quarkus.package.uber-jar" was provided
I've found
quarkus.package.type=uber-jar
as a property.
What I prefer is setting
<quarkus.package.type>uber-jar</quarkus.package.type>
in the pom properties.
https://github.com/fluentcodes/sandbox/tree/java-quarkus-empty

overriding property in maven plugin from command line

This maven command launches an older version (1.3.162) of the H2 database:
mvn -debug com.edugility:h2-maven-plugin:1.0:spawn
As recommended here, I'm trying to override a property of the plugin on the command line....so I can instead use a more recent h2 version:
mvn -debug -Dh2Version=1.4.200 com.edugility:h2-maven-plugin:1.0:spawn
This h2Version property with the older h2 version is defined here on github in the plugin's pom.
Here is the end of the verbose maven output
[DEBUG] Process arguments: [C:\java\jdk-9.0.4\bin\java, -cp, C:\Users\eoste\.m2\repository\com\h2database\h2\1.3.162\h2-1.3.162.jar, org.h2.tools.Server, -tcp, -tcpPassword, h2-maven-plugin, -tcpPort, 9092]
[INFO] H2 server spawned at tcp://localhost:9092
Not only does the old 1.3.162 version launch, but the there is zero mention anywhere of the h2Version property that I placed on the command line.
I tried moving the -Dh2Version parameter to the end of the command line. I also tried deleting the plugin from the local repo, to force a download so maybe the h2Version would then get reevaluated....none of these things worked.
This blog shows how to embed a dependency inside a plugin, but that's tons more complicated than my simple command line invocation.
What am I doing wrong?
Using windows 10, java 9, maven 3.6.2
What am I doing wrong?
1) Beware when you want to use plugins/libraries not maintained. The source code was not updated from about 8 years. That may have important issues.
2) To know how to use a maven plugin, don't look in the pom declaration. You can find some information in but you will find much more information in the mojo implementation/specification.
But in fact no, you should not even rely on that to understand how to use a plugin.
3) Indeed a Maven plugin may support configurable properties : directly in the pom.xml and even export them for command line usage. But that is not automatic.
But in both cases, that has to be foreseen by the plugin developer and it is generally documented on the plugin or the source repository homepage.
In fact in your case, if you go into the Mojo implementation : AbstractH2Mojo, you can see how the configuration is set.
All properties have default values in the mojo constructor.
protected AbstractH2Mojo() {
super();
final Service tcpService = new Service("tcp", Service.getDefaultPort("tcp"), false, false);
this.setServices(Collections.singletonList(tcpService));
this.setPort(Service.getDefaultPort("tcp"));
this.setShutdownPassword("h2-maven-plugin");
this.setJava(new File(new File(new File(System.getProperty("java.home")), "bin"), "java"));
}
The mojo empty constructor is first invoked, then all setter are invoked on the created instance.
It means that you can override any of these properties defined in that class at runtime by providing the property such as ${artifactIdPrefixWithoutMavenPlugin}.field.
Since the maven plugin is h2-maven-plugin, the prefix to refer is h2.
If you run that :
mvn -X com.edugility:h2-maven-plugin:1.0:spawn -Dh2.port=8084 -Dh2.useSSL=false
You could see in the output :
[DEBUG] Configuring mojo 'com.edugility:h2-maven-plugin:1.0:spawn' with basic configurator -->
[DEBUG] (s) port = 8084
[DEBUG] (s) shutdownHost = localhost
[DEBUG] (s) shutdownPassword = h2-maven-plugin
[DEBUG] (s) useSSL = false
[DEBUG] -- end configuration --
[DEBUG] Process arguments: [/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, /home/david/.m2/repository/com/h2database/h2/1.3.162/h2-1.3.162.jar, org.h2.tools.Server, -tcp, -tcpPassword, h2-maven-plugin, -tcpPort, 8084]
Concerning the h2 jar used, if you still look in the same class, you will see that part that retrieves the jar file from the classpath :
public final File getH2() {
final ProtectionDomain pd = Server.class.getProtectionDomain();
assert pd != null;
final CodeSource cs = pd.getCodeSource();
assert cs != null;
final URL location = cs.getLocation();
assert location != null;
try {
return new File(location.toURI());
} catch (final URISyntaxException wontHappen) {
throw (InternalError)new InternalError().initCause(wontHappen);
}
}
It means that you have no way to change the H2 JAR used : from the command line when you run the plugin or from the plugin declaration in the pom.xml, since no property is defined in the Mojo to achieve that.
if you will change the H2 version, you need to change the version embedded by the plugin. As a starter, you could try to fork the plugin GIT repository, change the h2 dependency used in the pom to match to your requirement and check whether in spite of the gap version, working with that plugin is a possible thing.
Note that you could add a new property of the Mojo to make it completely configurable such as :
mvn ... -Dh2Version=1.4.200
But in that case you will need to retrieve that. For example by performing a request to download the dependency from the m2 central repo for example.
And you should also ensure that only valid ranges of the h2 version are used.
I don't think you'll be able to do that using this particular maven-plugin. Here is another answer for someone that had a similar issue: How to pass parameter to Maven plugin from CLI?.
Basically, the h2Version property is not defined as a user property.
When you start the plugin, using the command that you've mentioned, there is an output for pre-defined configuration properties:
<configuration>
<allowOthers>${h2.allowOthers}</allowOthers>
<baseDirectory>${h2.baseDirectory}</baseDirectory>
<forceShutdown>${h2.forceShutdown}</forceShutdown>
<ifExists>${h2.ifExists}</ifExists>
<java>${h2.java}</java>
<port default-value="9092">${h2.port}</port>
<shutdownAllServers>${h2.shutdownAllServers}</shutdownAllServers>
<shutdownHost default-value="localhost">${h2.shutdownHost}</shutdownHost>
<shutdownPassword default-value="h2-maven-plugin">${h2.shutdownPassword}</shutdownPassword>
<trace>${h2.trace}</trace>
<useSSL>${h2.useSSL}</useSSL>
</configuration>
Only these properties could be defined by the user. For example, changing the running port:
mvn -debug com.edugility:h2-maven-plugin:1.0:spawn -Dport=9090

How to edit app.properties file with build parameter in Jenkins

I have a selenium script which I want to run from Jenkins. I have a properties file called app.properties. This file consists properties such as:
browser=chrome
I configured my project as parameterized so when I run my build, it asks for browser parameter. I want to select this parameter(for example firefox), so that it will change browser property in app.properties and will run the automation in Firefox.
Normally, when I change the browser property in app.properties in Intellij, my program runs with that browser. So there is nothing wrong with my program in that sense.
Is there a way to change app.properties with respect to my Jenkins build parameter and run the program with that configuration?
EDIT: I found the following solution:
Install surefire plugin.
Add a browser parameter.
In your property managing class, take browser parameter as
System.getProperty("browser");
From jenkins, configure a browser parameter
Invoke maven command: mvn test "-Dbrowser=${BROWSER}"
You can pass system properties to change configuration.
First you should configure your project to read both system properties and configuration file, where system properties will have higher priority. I'd recommend Apache Commons Composite Configuration. It can look like this:
CompositeConfiguration configuration = new CompositeConfiguration();
try {
configuration.addConfiguration(new SystemConfiguration());
configuration.addConfiguration(new PropertiesConfiguration("app.properties"));
} catch (ConfigurationException e) {
e.printStackTrace();
}
//Read your configuration values here
This way when you provide system property -Dbrowser=chrome it will override value from configuration file.
Secondly, you'll need to configure Jenkins job. Since you're passing a parameter you can use it in your build step definition:
mvn clean test -Dbroswer=${browser-param}
“The way parameters take effect is also different depending on the parameter type you choose ... String parameters are exposed as environment variables of the same name.”
https://wiki.jenkins.io/plugins/servlet/mobile?contentId=34930782#content/view/34930782

Jetty resource base different in Maven Nested Multi-module project of IntelliJ IDEA vs Eclipse

We have a nested multi-module project. Our developers are a mix of IntelliJ IDEA and Eclipse users.
When running a jetty server inside an inner module, it seems we need to set the resource base to different values depending on which IDE we are using.
For IntelliJ:
root.setResourceBase("myModule/src/main/webapp");
For Eclipse:
root.setResourceBase("src/main/webapp");
We don't want to have to tweak our IDEs to make it work, e.g. I don't want to have to change some setting in IntelliJ to make it work with the Eclipse version of the code.
Any ideas?
The short answer:
Your execution differences between Eclipse vs Intellij can be explained by having different PWD, or ${user.dir}, or working directory setups.
The better answer:
Don't use filesystem paths then.
Look up a known resource in that location via a Classloader.getResource() and then pass the parent directory into the root.setResourceBase()
Example:
Server server = new Server(8080);
// Figure out what path to serve content from
ClassLoader cl = WebAppContextFromClasspath.class.getClassLoader();
// We look for a file, as ClassLoader.getResource() is not
// designed to look for directories (we resolve the directory later)
URL f = cl.getResource("hello.html");
if (f == null)
{
throw new RuntimeException("Unable to find resource directory");
}
// Resolve file to directory
URI webRootUri = f.toURI().resolve("./").normalize();
System.err.println("WebRoot is " + webRootUri);
WebAppContext webapp = new WebAppContext();
webapp.setContextPath("/");
webapp.setWar(webRootUri.toASCIIString());
webapp.setParentLoaderPriority(true);
server.setHandler(webapp);
server.start();
server.join();
You can see this in the embedded-jetty-cookbook examples:
WebAppContextFromClasspath.java
ResourceHandlerFromClasspath.java
The other better answer:
Another approach is to find the src/main/webapp a few different ways depending on how it is being run
See the operational modes in the ServerMain.java in the embedded-jetty-live-war example.

Running Liquibase from inside a JAR. How do I tell it to look for the change set via classpath (also in the JAR) instead of filename?

I'm using Liquibase in my Scala application which is packaged in a JAR using sbt assembly. When the changelog location is supplied using a local file path, everything works fine. In order to deploy using only the JAR, I'd like it to look inside the JAR instead. How can I do this?
Current Setup:
build.sbt includes "org.liquibase" % "liquibase-core" % "3.3.5" as a dependency
Run:
val liquibaseChangeLog: String = (from config)
val liquibase = new liquibase.Liquibase(liquibaseChangeLog, resourceAccessor, liquibaseConnection)
liquibase.update(new Contexts())
I'd like to set liquibaseChangeLog to a classpath like org/my_org/my_project/changelog.xml but I get an error:
Exception in thread "main" liquibase.exception.ChangeLogParseException: org/my_org/my_project/changelog.xml does not exist
liquibase.parser.core.xml.XMLChangeLogSAXParser.parseToNode(XMLChangeLogSAXParser.java:100)
liquibase.parser.core.xml.AbstractChangeLogParser.parse(AbstractChangeLogParser.java:17)
Looking through the Liquibase source in this stack trace, I see that it explicitly wants a file. Is there a way to look in the JAR instead?

Categories