I am implementing a program where the core part is separated from the GUI and loaded at runtime as a service. I am struggling for weeks to have the implementation discovered at runtime. To try isolating the problem, I picked up a minimal ServiceLoader example from here https://reflectoring.io/service-provider-interface and inflated it into my project structure. I got the the conclusion that the javaxplugin is messing up something. The plugin is required for the GUI of my project, but is not required to run the code of the ServiceLoader. I am using version 0.0.10 of org.openjfx.javafxplugin, the last version is 0.0.13 but this causes the additional problem that the main class cannot be found anymore, so I am staying with the older version for the moment.
If the plugin is not requested in the build.gradle, the ServiceLoader code works, the implementation is loaded and the program gives the expected output. When the javaxplugin is requested in the build.gradle, the program does not work anymore.
Does anybody have a suggestion? I am really stuck because this is a JavaFX application and I need that plugin.
The project is a Gradle project with 3 subprojects (modules): the api, the implementation (core) and the application (GUI). The relevant files are here below.
API module-info.java:
module tlapi {
exports com.chesolver.spi;
exports com.chesolver;
}
The strange thing here is that if I enable the javafx pluging, the compiler rises the error
com.chesolver.spi.Library: module tlapi does not declare 'uses', which appears pretty wierd to me,since module tlapi is the api and com.chesolver.spi.Library is part of the interface contained in this module.
API build.gradle:
plugins {
id 'tubesheetlayout.java-library-conventions'
}
core module-info:
module tlcore {
requires tlapi;
}
core build.gradle:
plugins {
id 'tubesheetlayout.java-library-conventions'
id 'org.javamodularity.moduleplugin' version '1.8.9'
}
dependencies {
implementation project(':api')
}
application module-info:
module tlclient {
requires tlapi;
}
application build.gradle:
plugins {
id 'tubesheetlayout.java-application-conventions'
// *NOTICE* if uncommented the ServiceLoader code does not work
//id 'org.openjfx.javafxplugin' version '0.0.13'
//id 'org.javamodularity.moduleplugin' version '1.8.9'
}
dependencies {
implementation project(':api')
implementation project(':core')
}
application {
mainClass = "com.chesolver.library.LibraryClient"
}
Your main issue (incorrectly specifying service modules) and resources on how to fix it
For a Java platform modular application, you need to use Java Platform modular service definitions in your modules. This means using the provides and uses statements in your module-info.java.
Your code, and the service tutorial you linked, don't integrate services into the Java platform module system. The Maven module system discussed in the tutorial (and probably Gradle modules too), is something completely different.
To learn about modular services, study the "Services" sections in:
The State of the Module System.
The Jenkov module tutorial where it explains the provides and uses statements for module-info.java.
The ServiceLoader javadoc, which should be thoroughly reviewed and understood.
Baeldung provides example code for a modular service tutorial, which is simpler than what I have in this answer. But the Baeldung tutorial doesn't demonstrate binding multiple service implementations, jlinking, or using the loaded service modules from a JavaFX application, which is why I added an example for those things here.
Key Advice
My suggestion about this is: don't use the service mechanism unless you know you need it.
Example Solution
I know this question is about Gradle specifically, but I don't really use that tool. I will provide an alternate solution using Maven. Some aspects of it will carry directly over to Gradle, and others you will need to adapt.
The solution consists of a multi-module Maven project. Each of the submodules corresponds to a Java platform module. There is a parent pom.xml to specify all of the child modules and all of the child modules inherit from the parent.
The submodules involved are these:
shape-service: ShapeFactory interface that can create shapes.
circle-provider: a ShapeFactory implementation that provides circles.
square-provider: a ShapeFactory implementation that provides squares.
shape-app: a JavaFX application that loads the available ShapeFactory service providers and uses them to generate shapes.
Select the shape factory service provider from the combo box, then click "Create Shape" and the selected provider will be used to generate a shape, which will then be displayed.
I'll post the code here, unfortunately, there is a lot of it :-(
Building and running in Idea
You can import the maven project from the root directory into the Idea IDE. The project will load as a single Idea project, with multiple Idea project modules. When you run the main ShapeApplication class from the IDE, the IDE will automatically build all the modules and provide the services to your application.
Building and running from the command line
To build everything, run mvn clean install on the root of the project. To create a jlinked app change to the shape-app directory and run mvn javafx:jlink.
$ tree
.
├── circle-provider
│ ├── circle-provider.iml
│ ├── pom.xml
│ └── src
│ └── main
│ └── java
│ ├── com
│ │ └── example
│ │ └── shapeservice
│ │ └── circleprovider
│ │ └── CircleProvider.java
│ └── module-info.java
├── pom.xml
├── shape-app
│ ├── pom.xml
│ ├── shape-app.iml
│ └── src
│ └── main
│ └── java
│ ├── com
│ │ └── example
│ │ └── shapeapp
│ │ └── ShapeApplication.java
│ └── module-info.java
├── shape-service
│ ├── pom.xml
│ ├── shape-service.iml
│ └── src
│ └── main
│ └── java
│ ├── com
│ │ └── example
│ │ └── shapeservice
│ │ └── ShapeFactory.java
│ └── module-info.java
├── shapes.iml
└── square-provider
├── pom.xml
├── square-provider.iml
└── src
└── main
└── java
├── com
│ └── example
│ └── shapeservice
│ └── squareprovider
│ └── SquareProvider.java
└── module-info.java
The .iml are just idea module project files, you can ignore them.
Parent pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>shapes</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>shapes</name>
<modules>
<module>shape-service</module>
<module>circle-provider</module>
<module>square-provider</module>
<module>shape-app</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<javafx.version>19</javafx.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>19</source>
<target>19</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
shape-service
module com.example.shapeservice {
requires javafx.graphics;
exports com.example.shapeservice;
}
package com.example.shapeservice;
import javafx.scene.shape.Shape;
public interface ShapeFactory {
double PREF_SHAPE_SIZE = 40;
Shape createShape();
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>shape-service</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>com.example</groupId>
<artifactId>shapes</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>${javafx.version}</version>
</dependency>
</dependencies>
</project>
circle-provider
module com.example.shapeservice.circleprovider {
requires javafx.graphics;
requires com.example.shapeservice;
provides com.example.shapeservice.ShapeFactory
with com.example.shapeservice.circleprovider.CircleProvider;
}
package com.example.shapeservice.circleprovider;
import com.example.shapeservice.ShapeFactory;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Shape;
import java.util.concurrent.ThreadLocalRandom;
public class CircleProvider implements ShapeFactory {
private static final Color[] colors = {
Color.RED, Color.ORANGE, Color.YELLOW, Color.GREEN, Color.BLUE, Color.VIOLET
};
#Override
public Shape createShape() {
return new Circle(
PREF_SHAPE_SIZE / 2,
randomColor()
);
}
private static Color randomColor() {
return colors[ThreadLocalRandom.current().nextInt(colors.length)];
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>circle-provider</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>com.example</groupId>
<artifactId>shapes</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>shape-service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
square-provider
module com.example.shapeservice.squareprovider {
requires javafx.graphics;
requires com.example.shapeservice;
provides com.example.shapeservice.ShapeFactory
with com.example.shapeservice.squareprovider.SquareProvider;
}
package com.example.shapeservice.squareprovider;
import com.example.shapeservice.ShapeFactory;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import java.util.concurrent.ThreadLocalRandom;
public class SquareProvider implements ShapeFactory {
private static final Color[] colors = {
Color.CYAN, Color.MAGENTA, Color.YELLOW, Color.BLACK
};
#Override
public Shape createShape() {
return new Rectangle(
PREF_SHAPE_SIZE, PREF_SHAPE_SIZE,
randomColor()
);
}
private static Color randomColor() {
return colors[ThreadLocalRandom.current().nextInt(colors.length)];
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>square-provider</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>com.example</groupId>
<artifactId>shapes</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>shape-service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
shape-app
module com.example.shapeapp {
requires javafx.controls;
requires com.example.shapeservice;
uses com.example.shapeservice.ShapeFactory;
exports com.example.shapeapp;
}
package com.example.shapeapp;
import com.example.shapeservice.ShapeFactory;
import javafx.application.Application;
import javafx.collections.*;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;
import java.util.Comparator;
import java.util.ServiceLoader;
import java.util.stream.Collectors;
import static com.example.shapeservice.ShapeFactory.PREF_SHAPE_SIZE;
public class ShapeApplication extends Application {
#Override
public void start(Stage stage) {
ObservableList<ShapeFactory> shapeFactories = loadShapeFactories();
stage.setScene(new Scene(createUI(shapeFactories)));
stage.show();
}
private ObservableList<ShapeFactory> loadShapeFactories() {
ServiceLoader<ShapeFactory> loader = ServiceLoader.load(ShapeFactory.class);
return FXCollections.observableList(
loader.stream()
.map(
ServiceLoader.Provider::get
).sorted(
Comparator.comparing(
shapeFactory -> shapeFactory.getClass().getSimpleName()
)
).collect(
Collectors.toList()
)
);
}
private Pane createUI(ObservableList<ShapeFactory> shapeFactories) {
ComboBox<ShapeFactory> shapeCombo = new ComboBox<>(shapeFactories);
shapeCombo.setButtonCell(new ShapeFactoryCell());
shapeCombo.setCellFactory(param -> new ShapeFactoryCell());
StackPane shapeHolder = new StackPane();
shapeHolder.setPrefSize(PREF_SHAPE_SIZE, PREF_SHAPE_SIZE);
Button createShape = new Button("Create Shape");
createShape.setOnAction(e -> {
ShapeFactory currentShapeFactory = shapeCombo.getSelectionModel().getSelectedItem();
Shape newShape = currentShapeFactory.createShape();
shapeHolder.getChildren().setAll(newShape);
});
createShape.disableProperty().bind(
shapeCombo.getSelectionModel().selectedItemProperty().isNull()
);
HBox layout = new HBox(10, shapeCombo, createShape, shapeHolder);
layout.setPadding(new Insets(10));
layout.setAlignment(Pos.TOP_LEFT);
return layout;
}
private static class ShapeFactoryCell extends ListCell<ShapeFactory> {
#Override
protected void updateItem(ShapeFactory item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty) {
setText(item.getClass().getSimpleName());
} else {
setText(null);
}
}
}
public static void main(String[] args) {
launch();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>shape-app</artifactId>
<version>1.0-SNAPSHOT</version>
<name>shape-app</name>
<parent>
<groupId>com.example</groupId>
<artifactId>shapes</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>shape-service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>circle-provider</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>square-provider</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version>
<executions>
<execution>
<!-- Default configuration for running with: mvn clean javafx:run -->
<id>default-cli</id>
<configuration>
<mainClass>com.example.shapeapp/com.example.shapeapp.ShapeApplication</mainClass>
<launcher>shape-app</launcher>
<jlinkZipName>shape-app</jlinkZipName>
<jlinkImageName>shape-app</jlinkImageName>
<noManPages>true</noManPages>
<stripDebug>true</stripDebug>
<noHeaderFiles>true</noHeaderFiles>
<bindServices>true</bindServices>
<runtimePathOption>MODULEPATH</runtimePathOption>
<jlinkVerbose>true</jlinkVerbose>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Caveats
Splitting stuff up into modules and services like this makes things more complicated.
Many small things can go wrong, especially typos from copy and paste if you forget to change names.
The compiler won't pick up some typos because the service modules are dynamically discovered.
You can't mix package names across modules.
Linking with jlink is trickier because you need to ensure the services are on the jlinked module path and that you instruct jlink to bind them.
The tools are a bit obtuse in the error messages.
If not properly configured, the modules won't be found and the system will think that there are no matching services available.
Actually install all of your modules into your maven repository before each time you link it, otherwise it may pick up old versions or not find your software (this may not be actually necessary, but seemed the case in my experience).
Binding services will make your jlink image huge if you just use the default bind-services option. Apparently you can just bind listed services rather than all, but I could not find out how to do that with the javafx-maven-plugin. You can probably get more fine-grained control using the jlink command line than the maven plugin, though that would be more painful.
If you don't explicitly put a MODULEPATH setting in the javafx-maven-plugin, it won't find your service modules.
Always check the jlink verbose output to ensure that all of your expected services are being bound.
When working with a multi-module project like this, I strongly recommended keeping all of your modules at the same version number (centrally configured in the parent pom.xml), otherwise it is quite easy to link to obsolete versions. That isn't how this example project is setup though. To pin the version, define a shape.version property in the parent pom.xml and, wherever there is 1.0-SNAPSHOT, replace that with the ${shape-version}, then all projects will always use the same version.
The code assumes that everything is operating in a modular environment, with nothing running from the classpath. If you want to have services that work on the classpath, then you would need to do more work (e.g. to define service metadata in MANIFEST.MF files). I recommend only supporting the 100% modular environment unless you absolutely also have to support classpath execution.
I am sure there are more subtle and potential issues I haven't mentioned here.
Related
I have Git repository in which one repository contains two project as below.
Repository
- Project A
- Project B
My question is how can I build single project (Project A) through Jenkins.
When I use to git clone SSH URL in jenkinsfile it builds to whole repository but I want to build only Project A. So, could you please help me how can I do the same?
Please check below my jenkinsfile code:
node {
stage ('Build') {
git url: 'git#github.com:abc/sample.git'
withMaven {
sh "mvn clean install"
}
}
}
Is not a best practice to have several apps or libraries in one git repository. I advise you to use a git repository for each project. Also don't use spaces in sub projects name.
According to your comments, you will be able to compile your artifacts with one of the follow approaches:
Parent pom
As a extremely summary, you could build several maven projects with one hit. In your case you must add one pom.xml at root.
my-git-repository
├── pom.xml
├── project-a
├── pom.xml
├── project-b
├── pom.xml
This new pom.xml must be something like this:
?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>acme.foo</groupId>
<artifactId>bar</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<dependencies>
...
</dependencies>
<modules>
<module>../project-a</module>
<module>../project-b</module>
</modules>
<!-- Build -->
<build>
...
</build>
</project>
Finally, at root execute mvn clean package. As you will see, all the registered project in <modules> will be compiled.
More details about maven submodules here:
https://maven.apache.org/guides/mini/guide-multiple-modules.html
dir step
You are using node in your script, so it's not a declarative pipeline in which jenkinsfile is used. You are using a scripted pipeline
No matter if you are using scripted pipeline or declarative pipeline, a step called dir is the solution
node {
stage('Build') {
git url: 'git#github.com:abc/sample.git'
dir("Project-A") {
withMaven {
sh "mvn clean package"
}
}
dir("Project-B") {
withMaven {
sh "mvn clean package"
}
}
}
}
More details about dir step and jenkins pipelines type here:
https://www.jenkins.io/doc/pipeline/steps/workflow-basic-steps/
search dir: Change current directory section
Jenkins scripted pipeline or declarative pipeline
A business team outside of the development team needs some information out of a source controlled repo that we house source in. We're using Maven to perform our builds, and using Git for source control. What I want to do is get the reactor build order, and feed that into a script to automate some searching in Git to provide the business team what they need, but I need to make sure I'm telling Git to search the appropriate directories (and not just the root directory of the entire repo).
So obviously I can do something like mvn validate to provide me:
foo> mvn validate
[INFO] Reactor Build Order:
[INFO]
[INFO] foo
[INFO] sub_foo_1
[INFO] sub_foo_2
...
Except I have no way to take that and hand that off to Git for additional information because I don't know what directories any of the child modules live in.
Is there any way I can find out the path to the individual pom files that are part of the reactor build order?
I'm posting this as an answer, but it wouldn't have been my first choice; hoping somebody else will have something better. I was really hoping there was a native Maven way to do [something like] this.
Basically, I created a small plugin to print out the information.
Pom:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<groupId>foo.bar.baz.demo</groupId>
<version>1.2.3-SNAPSHOT</version>
<artifactId>test_plugin</artifactId>
<packaging>maven-plugin</packaging>
<modelVersion>4.0.0</modelVersion>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-project</artifactId>
<version>2.2.1</version>
</dependency>
</dependencies>
</project>
Code:
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.project.MavenProject;
#Mojo(name = "print_pom_dir")
public class TestMojo extends AbstractMojo {
public void execute() throws MojoExecutionException {
MavenProject object = (MavenProject) this.getPluginContext()
.get("project");
getLog().info(
"Pom directory: " + object.getBasedir().getAbsolutePath());
}
}
Where I can put a build execution in the parent such as:
<build>
<plugins>
<plugin>
<groupId>foo.bar.baz.demo</groupId>
<version>1.2.3-SNAPSHOT</version>
<artifactId>test_plugin</artifactId>
</plugin>
</plugins>
</plugins>
So all child modules inherit it.
Then all I have to do is run mvn foo.bar.baz.demo:test_plugin:1.2.3-SNAPSHOT:print_pom_dir from the root pom, and stdout gets my data for my to regexp out.
I wonder if putting it to a file or something in the target area might be cleaner, but I'm not too clear on best practices for Maven and whether or not changing the target directory for something not related to the build is standard, or how to get the root to know about the output from all child executions of the plugin for that matter.
I have some problem running SpringBoot application in Intellij. The #SpringBootApplication is located in the child module
└── conf
└── xml
└──beans.xml
└── source
└── core
└── common
└── MainApplication.java --> #SpringBootApplication
└── config
└── pom.xml --> parent
The MainApplication.java have annotation
#ImportResource("classpath:/beans.xml")
#SpringBootApplication
#EnableAutoConfiguration
#ComponentScan(basePackages = {"com.xxx.yyy"})
#EnableCaching
#ImportResource("classpath:/beans.xml")
public class ServiceApplication extends SpringBootServletInitializer implements WebApplicationInitializer // extends SpringBootServletInitializer implements WebApplicationInitializer
{
The "config" module has pom.xml, no java file. It only generate the resources
<artifactId>config</artifactId>
<properties>
<environment>DEV</environment>
<country>ID</country>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<build>
<filters>
<filter>../../build/server.properties</filter>
</filters>
<resources>
<resource>
<directory>../../conf</directory>
<filtering>true</filtering>
<excludes>
<exclude>**/**/log4j2.xml</exclude>
</excludes>
<targetPath>../../../../deploy/conf</targetPath>
</resource>
...
I tried to run configuration on Intellij, but it produce error FileNotFoundException for file beans.xml. This configuration already ran on Eclipse in other teammates. I want to run it on Intellij. I already import config module in Project Structure, and the conf folder already marked as Resource Root. Why its still not work? Or if I have to append change to existing java file or pom, its okay. as long the application can found its way to the resource folder. I dont need to build into war and run it, but I only want to run as SpringBoot application
Thank you. Any help will be appreciated.
i would say this is not a standard java project structure .... not good practice.
Go to File -> Project Structure -> Modules e.g.:
As you can see there u can edit the Paths to the Source, Resource, Test ... folders or add more than on.
perhaps this will help...
First: Remove the leading / from #ImportResource("classpath:/beans.xml").
This should be:
#ImportResource("classpath:beans.xml")
Second: Check that you beans.xml is copied to your project build directory when resources are processed.
The default location for the beans.xml would be target/classes/beans.xml.
Remove the leading / from #ImportResource("classpath:/beans.xml").
It should be like as : #ImportResource("classpath:beans.xml")
Check whether the source file beans.xml has been copied to the build directory or not:Path : /target/classess/beans.xml
If you have not placed the beans.xml in source folder, then provide the actual path or place it inside resource folder
A EAR application is composed with an EJB module + WAR module. Both are depending on Deltaspike (1.8.0) libraries.
While deploy the application under JBoss EAP 7.0, the output shows the following exception:
Caused by: java.util.ServiceConfigurationError: org.apache.deltaspike.core.spi.config.ConfigSourceProvider: Provider org.apache.deltaspike.core.impl.config.DefaultConfigSourceProvider not a subtype
at java.util.ServiceLoader.fail(ServiceLoader.java:239)
at java.util.ServiceLoader.access$300(ServiceLoader.java:185)
at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:376)
at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:404)
at java.util.ServiceLoader$1.next(ServiceLoader.java:480)
at org.apache.deltaspike.core.util.ServiceUtils.loadServiceImplementations(ServiceUtils.java:66)
The problem happen with CDI View extension ony from the WAR module. The initialization is correct from the EJB module.
What's going wrong with the WAR. Is it a configuration problem ?
Yes, it is a configuration problem as James R. Perkins suggested in the comments.
If you open the EAR (in any ZIP file viewer), you will notice that there are two copies of Deltaspike libraries:
application.ear
├── lib
│ ├── deltaspike-core-api-1.x.y.jar
│ └── deltaspike-core-impl-1.x.y.jar
├── web-app.war
| └── WEB-INF
│ └── lib
│ ├── deltaspike-core-api-1.w.z.jar
│ └── deltaspike-core-impl-1.w.z.jar
This confuses the classloader and causes the error above.
To fix this, mark Deltaspike libraries as <scope>provided</scope> in the WAR POM so that they will not be bundled into WAR WEB-INF/lib:
<dependency>
<groupId>org.apache.deltaspike.core</groupId>
<artifactId>deltaspike-core-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.deltaspike.core</groupId>
<artifactId>deltaspike-core-impl</artifactId>
<scope>provided</scope>
</dependency>
I've created a Maven plugin using AbstractMojo and I'm trying to test it.
I'm using the maven-plugin-testing-harness to do the testing and I'm having problems with injecting values for my plugin parameters.
I have the following pom.xml file (src/test/resources/pom.xml) for testing:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.vodori.pepper.docker.vm.unit</groupId>
<artifactId>test-pom</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Test VMStarter</name>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.vodori.common</groupId>
<artifactId>pepper-docker-vm</artifactId>
<version>1.0.1-SNAPSHOT</version>
<configuration>
<dockerSnapshotSite>pepper-demo-site</dockerSnapshotSite>
<dockerSnapshotVersion>3.6.11</dockerSnapshotVersion>
</configuration>
</plugin>
</plugins>
</build>
</project>
My Mojo looks like this (at the top):
public abstract class VMPlugin extends AbstractMojo {
/**
* Docker location
*/
#Parameter(required = true, property = "docker.path", defaultValue = "${env.DOCKER_LOCATION}")
String dockerPath;
public void setDockerPath(String dockerPath) {
this.dockerPath = dockerPath;
}
/**
* Docker VM Site name
*/
#Parameter(required = true, property = "docker.snapshot.site")
String dockerSnapshotSite;
/**
* Version of Docker snapshot
*/
#Parameter (required = true, property="docker.snapshot.version")
String dockerSnapshotVersion;
I'm using the #MojoRule approach for testing and my setup method looks like this:
#Before
public void setUp() throws Exception {
vmStarter = (VMStarter) rule.lookupMojo( "start-docker-vm", "src/test/resources/pom.xml" );
assertNotNull(vmStarter);
}
I use the setter for some of my testcases (the ones that test bad docker locations), but for my good path testing, I want to rely on the environment variable DOCKER_LOCATION for populating. However, for some reason, dockerPath is just showing up as null. It's as if the defaultValue is being ignored.
I've tried dumping System.getEnv() onto STDERR and I can see that DOCKER_LOCATION is indeed set.
What am I missing here? Why isn't my #Parameter getting populated correctly?
Where did you get the syntax defaultValue = "${env.DOCKER_LOCATION}" from?
env.* is a property and "you can use Maven properties in a pom.xml file or in any resource that is being processed by the Maven Resource plugin’s filtering features."
default-value requires an expression.
Guide to Developing Java Plugins, Introduction mentions: "(more can be found in the "Parameter Expressions" document)". But i didn't find such a document so far. Thx #khmarbaise: org.apache.maven.plugin.PluginParameterExpressionEvaluator.
help:expressions doesn't show ${env} with my Maven 3.2.1.
Though it's my experience that at least some, if not all, of the linked docs are not up-to-date and withhold latest enhancements.
Possible explanation from a program logic point of view: If a default value has to be set outside the scope of a program it can't be considered a default value. In the sense of Maven's Convention Over Configuration.
EDIT: Added link to Expressions API documentation.