local system wide modulepath for Java11 services - java

I've defined an API like
package eu.ngong.api;
...
import java.util.ServiceLoader;
public interface API {
// some specified service operation signatures
...
static List<API> getInstances() {
ServiceLoader<API> services = ServiceLoader.load(API.class);
List<API> list = new ArrayList<>();
services.iterator().forEachRemaining(list::add);
return list;
}
Now I defined and tested a provider module implementing this API with a module-info.java like
module eu.ngong.mdl {
...
requires eu.ngong.api;
provides eu.ngong.api.API with eu.ngong.mdl.Mdl;
}
And tested and installed it in local repository on my machine.
Now I did a new maven project for an executable jar that I like to distribute to my colleagues, but without the service implementation (they do their own).
It exposes a module-info.java like
module eu.ngong.UI {
requires eu.ngong.api;
...
}
and I am going to test it using mvn test.
Without anything, it will lead to
ERROR eu.ngong.UI.UITest - run tests failed: no API module found
Sure, because that service module to satisfy the API is not on the modulepath - namely eu.ngong.mdl.Mdl.
What would be the best way to get the service provider module on the modulepath - to be active at mvn test time?
Best solution for me would be to put something in the local maven configuration file .m2/repository/settings - but I could not identify how.
Or I can put some option on the mvn test command line - this is also the first time I am trying to do that and would need a hint.

Related

How can I run Cucumber Test runner from CLI without using Maven

I have seen the posts -
How to run cucumber file from command line
Cucumber java project without maven - how to run from command prompt if i am having Runner class
But the solutions given there are not very clear.
My CucumberRunner.java looks like -
#RunWith(Cucumber.class)
#CucumberOptions(
features = "src/test/resources/features",
tags="#Regression",
monochrome = false,
plugin = {"pretty",
"com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:",
"timeline:test-output-thread/",
"json:target/cucumber-reports/jsonReports/Cucumber.json",
"html:target/cucumber-reports/Cucumber.html"}
)
public class CucumberRunner {
}
Unfortunately because of security reasons I can not install maven on Linux machine. So I want to run this CucumberRunner file from CLI.
As suggested in the previous posts I tried below in my Cucumber class
public class CucumberRunner {
public static void main(String[] args){
Main.main(new String[]{"-g", "src/test/java/stepdefinitions", "src/test/resources/features/Validate.feature"});
}
}
But it gives me exception
Exception in thread "main" java.lang.NoClassDefFoundError: io/cucumber/core/cli/Main
at CucumberRunner.main(CucumberRunner.java:29)
Caused by: java.lang.ClassNotFoundException: io.cucumber.core.cli.Main
at java.net.URLClassLoader.findClass(URLClassLoader.java:387)
at java.lang.ClassLoader.loadClass(ClassLoader.java:419)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
at java.lang.ClassLoader.loadClass(ClassLoader.java:352)
... 1 more
But This also doesn't give any solution to run on the basis of Cucumber tags.
Any solution or pointer is appreciated.
Kind Regards,
Abhi
In order to run a Java application, you need to be able to resolve ALL dependencies at runtime. This means that, every single library referenced on the POM under the dependencies tag, must be somewhere in the machine where Selenium (Cucumber) is running. Likewise, if those libraries have dependencies of their own, you must also download them somewhere in the machine.
You have a few choices to access the dependent libraries. You need to reference all dependencies as external JARs (reside outside your application JAR) or you need to package them inside your application JAR. You can use a build tool like ANT to build your JAR and map your dependencies similar to how is done in Maven. Obviously the difference is that you need to do what I described before (your build tool will need to know where these JARs are physically located).
To execute, you will obviously need a main method. Because this is using Selenium (Cucumber), your Runner class is ran by JUnit or TestNG (depending on what you are using). After that, the rest is up to you. For example, you could simply create a batch file to launch your application, or you could use a tool like Launch4J to create an executable. Of course, the latter required creating an executable JAR.

JavaFX with Gradle error module not found

I'm creating a sample demo application with JavaFX in IntelliJ, but I need to use a library called the JavaFaker library. I'm using Gradle as the build system, but every time I try to add the library, either as the implementation in the build.gradle file, or via IntelliJ project structure options, the module.java file says error: module not found. I've already tried adding it to modules but nothing changes.
module-info.java
module com.example.demo1 {
requires javafx.controls;
requires javafx.fxml;
requires javafaker;
opens com.example.demo1 to javafx.fxml;
exports com.example.demo1;
}
build.gradle
plugins {
id 'java'
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.10'
id 'org.beryx.jlink' version '2.24.1'
}
group 'com.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
ext {
junitVersion = '5.8.2'
javaFakerVersion = '1.0.2'
}
sourceCompatibility = '17'
targetCompatibility = '17'
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
application {
mainModule = 'com.example.demo1'
mainClass = 'com.example.demo1.HelloApplication'
}
javafx {
version = '17.0.1'
modules = ['javafx.controls', 'javafx.fxml']
}
dependencies {
implementation("com.github.javafaker:javafaker:${javaFakerVersion}")
testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
}
test {
useJUnitPlatform()
}
jlink {
imageZip = project.file("${buildDir}/distributions/app-${javafx.platform.classifier}.zip") as RegularFile
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
launcher {
name = 'app'
}
}
jlinkZip {
group = 'distribution'
}
error message
> Task :HelloApplication.main() FAILED
Error occurred during initialization of boot layer
java.lang.module.FindException: Module javafaker not found, required by com.example.demo1
I tried for a while to get this to work with Gradle but was unable to. I don't know Gradle well, but unless you do, I don't advise trying it.
Alternate option: use a static import
I didn't try this, but this is suggested in another answer.
Before you try this, see:
What's the difference between requires and requires static in module declaration
It is IMO, a bit of a hack in this usage case. This makes the module optional at runtime. But, if the module is on the classpath instead of the module path its code can still be used. More information quoted from the linked answer:
A requires static clause expresses a dependency that is optional at
run time. That means at compile time the module system behaves exactly
as described above.
At run time, on the other hand, it mostly ignores requires static
clauses. If it encounters one, it does not resolve it. That means, if
an observable module is only referenced with requires static, it does
not make it into the module graph!
Alternate option: Non-modular project
You can fix this issue by making your project non-modular:
Delete your module-info.java file.
Run your application with JavaFX modules on the module-path.
The org.openjfx.javafxplugin you are already doing will help achieve this by specifying the modules to be used.
To execute the application directly in the IDE rather than through Gradle, you will need to specify the module options to the VM for the IDE execution configuration (information on that is in the getting started documentation at openjfx.io).
For packaging, switch to using the badass-runtime-plugin rather than the badass-jlink-plugin. This will package the application via jpackage rather than jlink (which cannot package non-modular applications or applications with automatic modules).
In the application block of your build file, you no longer need to specify the module for your application as you no longer have one.
While that means that your application is no longer modular, in this case, in my opinion, this is not such a big loss. The dependencies you are using are not well-defined modules, so you can't use jlink to create a package for your application, and you don't have the level of modular encapsulation and definition you would normally receive for fully modular projects.
For more information, see the Getting started instructions at:
https://openjfx.io/openjfx-docs/
Under the sections "Non-Modular with Gradle" for your selected IDE.
Alternate option: Using Maven
It is easy to get this to work with Maven.
Create a new JavaFX project
Choose Maven as your build system instead of Gradle.
Add the javafaker dependency to your pom.xml.
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>1.0.2</version>
</dependency>
Press the refresh icon in the Maven window to reimport the Maven project into the IDE.
Add the requires clause for the javafaker module into your module-info.java
requires javafaker;
Add the code to use javafaker to your app.
I don't have code to use javafaker, so I could not verify that the last step would work, but, give it a try . . .
Why you can receive this issue when using Gradle, but not Maven
Looking at the Gradle Documentation section "Using libraries that are not modules":
A third case are traditional libraries that provide no module information at all — for example commons-cli:commons-cli:1.4. Gradle puts such libraries on the classpath instead of the module path. The classpath is then treated as one module (the so called unnamed module) by Java.
This is the case with the javafaker dependency that you are using. It has no module-info.java and does not define the property Automatic-Module-Name in its manifest file (which are the other two cases in the section). Both the other cases result in Gradle putting the library on the module path, but the case you have means that it is on the class path.
This is a problem when you want to access the code from a named module that you define, which you have because you created a module-info.java.
Your module can only find code and resources of modules it requires (which need to be on the module path), so you add requires javafaker to the module-info.java, and get the following when you try to run through the IDE:
java.lang.module.FindException: Module javafaker not found, required by com.example.demo1
So you remove the requires javafaker from the module-info.java as advised by the Gradle documentation I linked and you get the following when you try to compile:
Package 'com.github.javafaker' is declared in module 'javafaker', but module 'com.example.demo1' does not read it
So you must place the library in the module-info to use it, but you can't place the library in module-info because Gradle puts in on the classpath -> catch-22.
There are workarounds to this such as providing VM arguments to allow access to the unnamed module (which is the classpath), or maybe modifying the module path handling of the Gradle build and/or IDE somehow (I don't know how), but they are pretty ugly.
On the other hand, for this case, Maven acts differently from Gradle, it places the dependent library on the module path, even if it does not have a module-info.java or Automatic-Module-Name defined. This means that it was (for me) much easier to set up and use.
Incidental advice on module naming
This is not an error, but note: Although module names with numbers in them are now allowed due to a change in the module system specification, it is probably best not to put numbers in module names to prevent the module name and version info being confused.
I've had a similar issue recently. Adding static to the requires statement helped however. Maybe this will fix your issue without having to switch to maven.
So you'd need to add: requires static javafaker;

jlink: service binding links many unnecessary modules

I face the problem that the service binding option of jlink links many, many modules, none of them seems to be necessary. These modules aren't linked when the service binding option is omitted.
Questions:
Q1: Do you see the same behavoir in your environment ?
Q2: Is this a bug or a desired behavoir ?
Q3: Why all these modules are linked ?
My application: The application is a simple service consisting of an interface, a provider and a consumer, each packed into a separate module, called modService, modProvider, modConsumer (details below).
OS: Windows 10
Jlink without --bind-services yields the expected result:
jlink --module-path "mods;%JAVA_HOME%\jmods"
--add-modules modConsumer
--output myRuntime
java --list-modules
java.base#9
modConsumer
modService
When the --bind-services option is applied, I would expect that in addition the module modProvider should be linked. However, see what happens (the three custom modules are at the end):
jlink --module-path "mods;%JAVA_HOME%\jmods"
--bind-services
--add-modules modConsumer
--output myRuntime
java --list-modules
java.base#9
java.compiler#9
java.datatransfer#9
java.desktop#9
java.logging#9
java.management#9
java.management.rmi#9
java.naming#9
java.prefs#9
java.rmi#9
java.scripting#9
java.security.jgss#9
java.security.sasl#9
java.smartcardio#9
java.xml#9
java.xml.crypto#9
jdk.accessibility#9
jdk.charsets#9
jdk.compiler#9
jdk.crypto.cryptoki#9
jdk.crypto.ec#9
jdk.crypto.mscapi#9
jdk.deploy#9
jdk.dynalink#9
jdk.internal.opt#9
jdk.jartool#9
jdk.javadoc#9
jdk.jdeps#9
jdk.jfr#9
jdk.jlink#9
jdk.localedata#9
jdk.management#9
jdk.management.cmm#9
jdk.management.jfr#9
jdk.naming.dns#9
jdk.naming.rmi#9
jdk.scripting.nashorn#9
jdk.security.auth#9
jdk.security.jgss#9
jdk.unsupported#9
jdk.zipfs#9
modConsumer
modProvider
modService
I have no clue why all these modules are linked because the provider just returns a string so that no other jdk module than java.base should be needed.
Below are the Java artifacts:
package test.service;
public interface HelloService {
public String sayHello();
}
package test.provider;
import test.service;
public class HelloProvider implements HelloService {
#Override public String sayHello() { return "Hello!"; }
}
package test.consumer;
import test.service;
import java.util.ServiceLoader;
public class HelloConsumer {
public static void main(String... args) {
ServiceLoader.load(HelloService.class).forEach(s -> System.out.println(s.sayHello()));
}
}
module modService {
exports test.service;
}
module modProvider {
requires modService;
provides test.service.HelloService with test.provider.HelloProvider;
}
module modConsumer {
requires modService;
uses test.service.HelloService;
}
Any help is appreciated.
Short version
Q1: Yes.
Q2: Desired behavior
Q3: Because you told jlink so with --bind-services 😉
Long Version
By default jlink does not bind services, to keep the created runtime as small as possible. That changes with --bind-services, about which the documentation says
Link service provider modules and their dependencies.
That mirrors the behavior during regular module resolution where, after all dependencies have been resolved, all modules that provide a service used by those modules are included in the readability graph.
The same happens in your case, so all modules providing services used by java.base, modConsumer, and modService are included in the image. As you have found out, that are quite a lot.
Solution
If you want to avoid that, you have to forego --bind-services and instead explicitly list the providers that you want to see in your image:
jlink --module-path "mods;%JAVA_HOME%\jmods"
--add-modules modConsumer,modProvider
--output myRuntime
As stated in the jlink documentation. The
--bind-services
Link service provider modules and their dependencies.
further, a sample in the same illustrates that the
option will link the modules resolved from root modules with service
binding; see the
Configuration.resolveAndBind
method.
From your previous command the root module and the ones resolved in your module graph by default are :
java.base#9
modConsumer
modService
and further, the other modules listed while making use of --bind-services flag are resolved via the java.base module.
I would expect that in addition the module modProvider should be
linked
As suggested by nicolai, you can add the provider module and ensure that it is resolved as well in the module graph.
--add-modules modConsumer,modProvider
Thinking out loud. 1. The current process for finding providers is iterative. 2. Is it possible to specify modules for which one to look for while finding service providers explicitly?

Can Play 2.3.x be used without Activator (and with maven)?

I have two related questions here.
In Play 2.2.x, the distribution was bundled as a zip file, and available for download through the maven repository http://downloads.typesafe.com/play/2.2.x/play-2.2.x.zip. This meant that you could use a pom.xml and embed play into your app without needing to use sbt. Given 2.3.x has shifted to the activator model, is it still possible to use it with maven?
And secondly, is it possible to use play 2.3.x without activator at all? (I know they have a sbt plugin for play, but that seems very complex as well).
Thanks!
Activator is only needed to create the empty template project, which you could also do by hand if you know a bit about play. After that empty project is created all you need is sbt (which actually is a pretty central part of activator).
With play 2.3 the distribution model changed from the one big zip-file to regular ivy/maven dependencies, so you could possibly get all dependencies right from a maven project. The problem is that the sbt play setup does so much more: template compilation, routes DSL compilation, hot reloading, asset pipeline stuff, so I don't think maven actually is an option.
Yes.
Example on Github
package io.github.alancnet
import java.io.File
import play.api.{Environment, ApplicationLoader}
object PlayTest {
class Dummy{}
def main(args:Array[String]):Unit = {
def startWebServer = {
val environment = new Environment(
new File("."),
classOf[Dummy].getClassLoader,
play.api.Mode.Dev
)
val context = play.api.ApplicationLoader.createContext(environment)
val application = ApplicationLoader(context).load(context)
play.api.Play.start(application)
play.core.server.NettyServer.fromApplication(
application
)
}
startWebServer
}
}

PlayPlugin onApplicationStart does not work

I try to write a small playframework-module and a plugin in it, and want be sure that it will start when I'm starting up the application.
[myapp] -- uses --> [registration(module)].[plugin(RegistrtionPlugin)]
There is my plugin class I've put to /src/play/modules/registration int he module source
public class RegistrationPlugin extends PlayPlugin {
public void onApplicationStart() {
Logger.info("Yeeha, firstmodule started");
}
}
I have dependency to this module form my application, when i do play deps - it shows me that everything good. Or when i type in console in my app-home folder: play registration:hello - it replies me 'Hello' back.
I've got this code from PlayFramework Cook book (chapter 5), but can not make it working as I expect.
Have you added a play.plugins file to your module src folder configuring the RegistrationPlugin plugin?
The play.plugins of e.g. the spring plugins looks like this:
1000:play.modules.spring.SpringPlugin
You might also take a deeper look at the spring plugin:
At last before the module can be used following command has to be executed:
play build-module
Try to move your RegistrationPlugin to regular /app folder
and don't forget to include path to plugin in /src/play.plugins file.
f.e.
1000:RegistrationPlugin
if RegistrationPlugin is in default package

Categories

Resources