Deltaspike init failed under JBoss EAP 7 - jboss-eap-7

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>

Related

ServiceLoader does not locate implementation when Gradle javafxplugin is used

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.

Maven dependency - if both direct and transtive depedency of same artifact are present, which will be used

if both direct and transtive depedency of same artifact are present in pom.xml, which will be used
Below is snippet from my pom.xml in the same order-
<dependency>
<groupId>com.browserstack</groupId>
<artifactId>browserstack-local-java</artifactId>
<version>1.0.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>```
browserstack-local-java has transitive dependency - junit 4.11
junit 4.11 has direct vulnerability. If I scan this project for vulnerability -
which version of junit will be considered?
Will it be junit 4.11 which is transitive to broswerstack-local-java
or the later one which is 4.13.1 ?
Thanks
This is described in Transitive Dependencies
Dependency mediation - this determines what version of an artifact will be chosen when multiple versions are encountered as dependencies. Maven picks the "nearest definition". That is, it uses the version of the closest dependency to your project in the tree of dependencies. You can always guarantee a version by declaring it explicitly in your project's POM. Note that if two dependency versions are at the same depth in the dependency tree, the first declaration wins.
"nearest definition" means that the version used will be the closest one to your project in the tree of dependencies. Consider this tree of dependencies:
A
├── B
│ └── C
│ └── D 2.0
└── E
└── D 1.0
In text, dependencies for A, B, and C are defined as A -> B -> C -> D 2.0 and A -> E -> D 1.0, then D 1.0 will be used when building A because the path from A to D through E is shorter. You could explicitly add a dependency to D 2.0 in A to force the use of D 2.0, as shown here:
A
├── B
│ └── C
│ └── D 2.0
├── E
│ └── D 1.0
│
└── D 2.0

FileNotFoundException resource file from external directory maven intellij

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

Maven pom dependencies between app and common

Every applications has its own pom.xml and root has its own pom.xml. common is in every web app and it has its own pom.xml. I want to get different values from common pom.xml based on webapp loads. For instance common should give value1 for web-ap1 and same common should give value2 for web-app2. How should i get different value from common pom ?
my-project
+ pom.xml
+-- common
+ pom.xml
+-- web-app-1
+ pom.xml
+-- web-app-2
+ pom.xml ?

How to set up manifest class-path properly in maven-war-plugin

I've used the maven-ear-plugin with the maven-war-plugin and maven-ejb-plugin to successfully deploy and run an application packaged as EAR to Jboss AS7.
.
|-- META-INF
| |-- application.xml
| |-- MANIFEST.MF
| `-- maven
| `-- com.patrac
| `-- Patrac-ear
| |-- pom.properties
| `-- pom.xml
|-- Patrac-ejb-1.0-SNAPSHOT.jar
`-- Patrac-web-1.0-SNAPSHOT.war
In the application source code directories, the poms are located as follows:
.
|
|-- Patrac-ear
| `-- pom.xml
|-- Patrac-ejb
| `-- pom.xml
|-- Patrac-web
| `-- pom.xml
`-- pom.xml
I can't figure out how to stop a few annoying warning messages when I deploy the application:
12:32:03,958 WARN [org.jboss.as.server.deployment] (MSC service thread 1-2) Class Path entry richfaces-components-ui-4.0.0.Final.jar in "/content/Patrac.ear/Patrac-web-1.0-SNAPSHOT.war" does not point to a valid jar for a Class-Path reference.
12:32:03,970 WARN [org.jboss.as.server.deployment] (MSC service thread 1-2) Class Path entry richfaces-components-api-4.0.0.Final.jar in "/content/Patrac.ear/Patrac-web-1.0-SNAPSHOT.war" does not point to a valid jar for a Class-Path reference.
12:32:03,984 WARN [org.jboss.as.server.deployment] (MSC service thread 1-2) Class Path entry richfaces-core-api-4.0.0.Final.jar in "/content/Patrac.ear/Patrac-web-1.0-SNAPSHOT.war" does not point to a valid jar for a Class-Path reference.
12:32:03,989 WARN [org.jboss.as.server.deployment] (MSC service thread 1-2) Class Path entry richfaces-core-impl-4.0.0.Final.jar in "/content/Patrac.ear/Patrac-web-1.0-SNAPSHOT.war" does not point to a valid jar for a Class-Path reference.
Patrac-web-1.0-SNAPSHOT.war!META-INF/MANIFEST.MF looks like this:
Manifest-Version: 1.0
Built-By: pgarner
Build-Jdk: 1.7.0_02
Class-Path: Patrac-ejb-1.0-SNAPSHOT.jar richfaces-components-ui-4.0.0.
Final.jar richfaces-components-api-4.0.0.Final.jar richfaces-core-api
-4.0.0.Final.jar richfaces-core-impl-4.0.0.Final.jar cssparser-0.9.5.
jar sac-1.3.jar guava-r08.jar
Created-By: Apache Maven
Archiver-Version: Plexus Archiver
The ejb class-path entry needs to be present for the EJB module, for portability, and the richfaces, cssparser and guava class-path entries should NOT be in the WAR's manifest.
The problem is that my WAR depends on all of the JARs, some of which live in WEB-INF/lib (RichFaces), and one JAR, Patrac-ejb-1.0-SNAPSHOT.jar, that lives in the root directory of the EAR. Each dependency needs to be entered in Patrac-web/pom.xml but NOT each of the dependencies should appear in the manifest.
Maven puts the JARs in the correct places, but it puts Class-Path entries for ALL of the JARs into the manifest. It should not do this. It should ONLY put an entry in for Patrac-ejb-1.0-SNAPSHOT.jar.
<!--
According to Java EE 6 spec, the application is portable if
Patrac-web.war's META-INF/MANIFEST.MF contains a Class-Path entry
for Patrac-ejb-1.0-SNAPSHOT.jar.
<optional>true</optional> is the flag that maven-war-plugin uses
to put the entry in MANIFEST.MF without copying Patrac-ejb-1.0-SNAPSHOT.jar
into WEB-INF/lib. This is what I want.
<scope>provided</scope> would cause maven-war-plugin to NEITHER
put the entry in MANIFEST.MF nor copy Patrac-ejb.jar into WEB-INF/lib,
which would not be good.
No tag at all would cause maven-war-plugin to BOTH put the entry in
MANIFEST.MF and copy Patrac-ejb.jar into WEB-INF/lib, which would
also not be good.
-->
<dependency>
<groupId>com.patrac</groupId>
<artifactId>Patrac-ejb</artifactId>
<type>ejb</type>
<optional>true</optional>
</dependency>
<!--
These two dependencies are used to copy
the other JAR files into WEB-INF/lib and there
should not be any class-path entries for such
JARs in MANIFEST.MF, in order to avoid the
error messages.
-->
<dependency>
<groupId>org.richfaces.ui</groupId>
<artifactId>richfaces-components-ui</artifactId>
</dependency>
<dependency>
<groupId>org.richfaces.core</groupId>
<artifactId>richfaces-core-impl</artifactId>
</dependency>
I'm using the most recent maven-war-plugin version, 2.2. How do I tell the maven-war-plugin to put the "non-ejb" JARs into WEB-INF/lib while not putting class-path entries in MANIFEST.MF?
Any advice or pointers you have are greatly appreciated.
References:
Maven War Plugin
Maven Ear Plugin
The Maven archiver, used by the Maven WAR plugin, provides a means to generate Class-Path entries in the WAR's manifest but unfortunately the archiver takes an all-or-nothing approach. Once you pass addClassPath=true to the archive configuration, the archiver puts Class-Path entries for all of the WAR's required- and optional dependencies into the manifest.
However, some entries simply do not belong in there. Class-Path entries are used to denote "Download Extensions," or references to JARs external to the WAR. JARs located in WEB-INF/lib should not, therefore, have Class-Path entries in the WAR's manifest. Maven's War plugin breaks this rule when you set addClassPath=true in the archiver.
Moreover, when you pass addClassPath=true to Maven's archiver, it gives all of the Class-Path entries the same directory prefix -- no matter where the dependencies are located within the EAR. This causes problems when optional- and required dependencies are located in separate locations such as the EAR root directory, EAR lib and WAR WEB-INF/lib.
Naturally, when one deploys an EAR whose WAR manifest contains the above errors JBoss throws warnings if the WAR class loader can ultimately find a dependency (JARs located in WEB-INF/lib) or errors if the class path prefix is wrong (e.g. on an EJB JAR or a dependency in the EAR lib directory).
Therefore, if your WAR, like mine, depends on an EJB module, located at the root of the EAR, and any number of dependencies that are located inWEB-INF/lib, Maven archiver will generate Class-Path entries for all of the dependencies and they will all have the same prefix regardless of their location within the EAR.
Not good.
The problem would be ameliorated somewhat if the archiver would provide a means to exclude Class-Path entries of the JARs located in WEB-INF. But it does not.
Here's a summary of the outcome for each dependency setting when one uses addClassPath=true:
Setting Generate Copy JAR into
Class-Path WEB-INF/lib
Entry
1. <scope>provided</scope> NO NO
2. <optional>true</optional> YES NO
3. (no option -- default) YES YES
4. ????? NO YES
What is needed is coverage for situation #4, above: do not create a Class-Path entry and, yes, do copy the JAR into WEB-INF/lib. Maven archiver should implement this behavior by default.
The best solution I can think of is to use brute force and turn off the Maven archiver's automatic Class-Path entry generation. Instead, I explicitly create a Class-Path entry for the EJB module in the WAR's manifest using the archiver's manifestEntries option. The way to do this is to remove the following from Patrac-web/pom.xml:
<manifest>
<addClasspath>true</addClasspath>
</manifest>
and replace it with this:
<manifestEntries>
<Class-Path>Patrac-ejb-${project.version}.jar</Class-Path>
</manifestEntries>
Using this configuration only one Class-Path entry, for the EJB module, is created explicitly.
For those JARs with need to be excluded from MANIFEST and from WEB-INF/lib you need to mark them as <scope>provided</scope>. That means JAR files will be provided by the container (and that is EAR in your case).
For other options check WAR manifest guide.
The ejb class-path entry needs to be present for the EJB module …
The class-path only needs to be present for EJBs modules (in the root of the EAR) who reference other EJB modules (in the root of the same EAR)
read specifically about skinny wars
http://maven.apache.org/plugins/maven-war-plugin/examples/skinny-wars.html

Resources