I have an interesting problem. I have two projects, which both depend on different versions of a library. The individual projects cannot change their dependencies, due to version constraints.
So:
Project A depends on lib version 1
Project B depends on lib version 2
lib version 2 is binary compatible with version 1, but has additional functionality so we don't want to update project A to depend on version2.
When running tests that only involve project A, then the class path is perfect, it only contains version 1 of lib.
When running tests that only involve project B, then the class path is perfect, it only contains version 2 of lib.
When running tests that involve both projects A and B, then the class path is broken, as it contains both version 1 and 2 of lib.
Is there any way to say "use this library for compiling only, and don't export for the runtime class path"? This is in some ways related (but opposite) to the exported flag on a JAR file, since that exports it to projects that you depend on as a compile dependency. I want to not export it as a runtime dependency.
When running from the command line this all works fine for us, it is only within Eclipse that we are seeing this problem.
It is possible to specify a classpath for a junit launch configuration via the junit run configuration dialog. See here section "Customizing a Test Configuration": https://help.eclipse.org/neon/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2FgettingStarted%2Fqs-junit.htm
(image from https://help.eclipse.org/neon/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2FgettingStarted%2Fqs-junit.htm)
The new version of the library needs to be added to the classpath before the default element so that during class loading the new library version will be seen first.
After you have configured the correct classpath export the launch configuration to the workspace via Export ... -> Launch Configurations. Then you will be able to run it with right clicking on it and selecting Run as -> <Your Lauch Config name>
Related
I updated an Eclipse RCP Project from Eclipse 2019-12 to 2020-03. After the update I get build errors on export, like the following:
"The package javax.xml.parsers is accessible from more than one module: , java.xml".
I am using Java 11, no module-info.java file, the Eclipse 3 Compatibility Layer and the eclipse product export wizard in my project.
The error message basically is clear to me. It detects some class twice on classpath, which is not allowed. Once the class is provided by JDK module "java.xml". The second copy comes from javax.xml osgi bundle.
My problem is that both of the packages can not be removed. The JDK module has a lot of dependent JDK modules that are actively used in code and I don't think that they can be supplemented by some libraries. The OSGI bundle is used by a lot of very basic eclipse bundles. So it can also not be removed.
So I have two questions:
Why does Eclipse RCP break projects with its new version? Was that considered a bug before? (I did not find a ticket)
How can I get my project to run again without really big changes?
Edit 1: Added reproducible example:
https://workupload.com/file/wKUZTXJXsR9
Edit 2: See also Eclipse Bug Ticket:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=562761
I created a minimum example. One can see the problem by exporting the product via wizard. I used DocumentBuilderFactory from javax.xml in Application.java to trigger the problem.
I found out a few things:
A plugin dependency to org.eclipse.help.ui is needed to trigger the problem. Although also other plugins have dependencies to javax.xml plugin.
Eclipse tells you the problem in Editor if I you add a direct dependency to javax.xml plugin. But we have only a transitive dependency to it and therefor get no hint.
That raises some new questions:
It seems that org.eclipse.help.ui plugin causes the problem, but we need it in our application. Is this a possible bug? Are there alternatives?
Why does eclipse editor not show the problem if I only have a transitive dependency to javax.xml plugin? Also a possible bug?
I found a workaround for this problem: When using a target definition (*.target file) instead of the Running Platform (automatic target definition of the Eclipse IDE) the error does simply not occur! I don't know exactly what the difference is between these two targets, since they both contain the "javax.xml" bundle. But when using the target file the "javax.xml" bundle is not added to the product file when automatically adding the dependencies and it does not complain later on that this bundle is needed "javax.xml". Somehow when using the target file the "javax.xml" is simply not needed and then no error occurs.
I think there is a bug in Eclipse RCP, that causes this problem when using the Running Platform. But I suggest using a target definition file anyways, since this brings a lot of other advantages.
I have a project 'java11-core' that generates a test jar artifact to share with project 'java11-app'. These projects build fine with command line Maven, but within Eclipse, the classes shared in the test jar cannot be found.
Version Info:
Apache Maven 3.6.0 (command line and Eclipse)
Java version: 11.0.1, vendor: Oracle Corporation
Eclipse IDE: Version: 2018-09 (4.9.0)
M2E Plugin: 1.9.1.20180912-1601
I originally created these to projects as tradition non-JPMS projects. These projects compiled and ran tests normally as expected. After I added module-info.java to both java11-core and java11-app, the Eclipse compiler could not recognize the shared test files from the core project.
Here is a snapshot of the package explorer for an overview of the project structure.
The added java11-app and java11-core module-info contents respectively:
module com.java11.app {
exports com.java11.app;
requires com.java11.core;
}
module com.java11.core {
exports com.java11.core;
}
As you can see, I do not export the test utilities package from com.java11.core. I do not want to export the test packages because this would make the test classes publicly available. I also do not wish to introduce a new test project, because in real-world scenarios, this is very likely to require cyclic dependencies between test utilities and the projects they assist in testing.
Build errors for in AppTest.java. The failure reported by Eclipse is interesting is that it does not claim it cannot find the CoreTestUtil class, but rather:
The type com.java11.test.core.util.CoreTestUtil is not accessible AppTest.java /java11-app/src/test/java/com/java11/app line 8 Java Problem
CoreTestUtil cannot be resolved AppTest.java /java11-app/src/test/java/com/java11/app line 21 Java Problem
My assumption is that the lack of an export for this package from java11-core and/or the lack of a requires for this package in java11-app make eclipse believe the access is restricted, even though the classes exist in a separate test-jar.
The module path for java11-app shows it includes java11-core as a module, and the Without test code is set to No.
I know I am working with newly release features and suspect that sharing test classes across Eclipse JPMS project is not yet supported. But, I am not sure where to look (Eclipse? M2E plugin) for an update on it being supported. I am also not aware of a work-around that would allow me to be productive while adopting JPMS for my software projects.
For those that believe test utilities should not be shared this way...
This subject has been characterized as a best-practice issue that should be resolved by refactoring test utilities into a separate module. I respect this perspective, but in attempting to follow that guidance, I found myself being forced to violate other best-practices, including DRY (Don't Repeat Yourself), and cyclic dependencies between packages.
It is common for a test utility to emerge while developing a module that both assists in effective testing of that module, as well as depends on that module. This creates a cycle if those utilities are pulled out to separate module. Furthermore, some of these utilities can be equally useful when testing other modules that depend upon that module. This creates duplicate code if those utilities are copied to a new test module for dependents. This reasoning may have been why Maven 'test-jar' support was originally added.
Eclipse does not support multiple module-info per project: in whatever source folder (main or test), you must only have one module-info.
From Eclipse point of view, your only luck is to create a new Java project referencing the other and with its proper module-info/exports:
module mod.a {
exports com.example.a;
// com.example.a.Main
}
module mod.a.tests { // (1)
exports com.example.a.tests;
// com.example.a.tests.MainUtils calling com.example.a.Main
requires mod.a;
}
In case (1), you will have problems if you don't use mod.a.tests: Java will never find com.example.a.Main, probably because the second project shadows the first project.
I am not an OSGI expert, but I think that's one of those reason for why most Eclipse plugin do have a main and test projects: org.eclipse.m2e.core is patched by org.eclipse.m2e.core.tests
However module-info does not have any knowledge of "patches": you may patch a module on command line (java --patch-module), but not in module-info itself: perhaps Eclipse could do that on your behalf, but it don't.
As you can see, two project in Eclipse = two Maven module.
In the case of Maven, you can certainly create other artefacts with the same build (and I do think it tends to pollute the dependencies, because every time your secondary artefacts will requires a dependency, it would have to go to the common scope).
This can be done using maven-compiler-plugin, maven-shade-plugin and maven-jar-plugin:
I think you should not rely test-jar because you want to emulate the --patch-module of Java by merging the classes and test-classes directories.
You don't want to import this project in Eclipse due to multiple module-info; or you must ensure that its module-info is only visible to Maven (you can use a profile + m2e.version do detect m2e and disable it).
I fully agree with you. Why should I only use the src-main code from some core module when I also could inherit some src-test functionalities?
But how to handle the scope problem? When I use the "test"-scope I loose the relation to the src-main code. When I dont use the test scope I loose the relation to the src-test code.
My core test code does not change very often, so to get the stuff working in Eclipse
I install the test-jar to my local repository and everything works fine.
Java 9 / 10. I have been struggling with a simple project for more than a week.
As you can see in the picture, I want to use commons-collections as an automatic module (I have tried to add it with maven but that did not work out well).
So, I have red that I need to put the jar onto the module-path. Where does IntelliJ take this modulepath from? How can I tell the IDE to add commons-collections into the project so that
1. the compiler can find it at compile time and
2. Maven can find it at build time?
Anyone can help?
EDIT:
I have tried to add it in the project-structure dialog as a module dependency in all kinds of different combinations. I have literally tried hundreds of things, moved the jar around in the structure and I cannot find a simple enough doc to tell me how to do this.
I have used compiler options to add "--module-path automatic" (module specific and general compile options) in order to make IDEA find the thing and let Java make an automatic module out of it.
You need to add a library entry first, to make it available under Modules:
Step 1: Add a library (Add -> Java -> jar file)
Step 2: Select the module (remember to click "Apply")
After that, the module-info.java file will be successfully validated:
Intellij uses a module path if you run a program from an (intellij) module containing module-info.java, otherwise it will use a classpath.
I tried importing common4 as a module, it does seem to work for me, but I had to use a different 'requires' argument as compared to yours. Your 'requires' is 'commons.collections4', mine is 'org.apache.commons.collections4' (check the commons4 manifest entry for the highlighted Automatic-Module-Name and use that instead).
If the Automatic-Module-Name is missing from the commons4 manifest (it is absent from version 4.1 and earlier), Java may not detect the jar as a module if the name contains digits or illegal characters. Some maven repository jars therefore will not work and intelli will not see those jars as modules.
You can also check for a bad filename by using the following command:
jar --file=/path/to/jar --describe-module
If the command fails, it's likely that the jar does not have an Automatic-Module-Name entry and that the filename is poorly named.
ok
jar --file=C:\temp\jigsaw1-1.0.jar --describe-module
bad
jar --file=C:\temp\jigsaw1.0.jar --describe-module`
'jigsaw1.0: Invalid module name: '0' is not a Java identifier'
Some maven jars may therefore fail to be detected as modules as they tend to look similar.
I'd like to run tests with JUnit 5 on Java 9 modular project in Eclipse, with no Maven, Gradle or all that fancy stuff. So I have src/main/java path where module-info.java and module's packages live and also src/test/java where all the test classes are. Id est business as usual, prior to the Jigsaw module system. I have Eclipse Oxygen.3a (4.7.3a) an Java 10.0.1.
I've seen some video from Eclipse showing, how to add JUnit test to modular project, but this flabbergasted me deeply: they put required keyword into module-info.java of a module, binding it to JUnit module. Is that actually even correct?
I've seen also all these --patch-module/--add-reads solutions (when we're talking about working in a console) and it seems like it's the proper way to do it, but I have no idea, how to achieve that in Eclipse without binding module under test to JUnit module. Is that even possible in Eclipse (without Maven and s.o)?
I tried to solve this problem for quite a while, too. My approach is to add a filter to the source code directory for src/main/java that filters out the module-info.java. This allows to have a different module-info.java in src/test/java. It will be this one that gets copied to the output folder. This way you can run your unit tests from within the IDE and use the other one for the final build. However, you need to keep the content of the one in src/main/java updated yourself.
Right click on the project > Properties > Java Build Path > Source
Select the src/main/java entry, click Edit > Next > Exclusion Patterns > Add
I'm developing a project in Eclipse JDT, that has optional dependencies on OSGI - that is, it supports being used as an OSGI bundle, and if it is used as a bundle inside an OSGI environment, it will also reference some classes from OSGI.
Now I want to write JUnit test cases for both running inside OSGI and running without OSGI, included in two different test suites. The OSGI tests are run as JUnit Pulg-in Tests and the Non-OSGI tests should be run as normal JUnit Tests.
Now I have the problem, that I couldn't find any way in Eclipse to exactly specify the classpath for the JUnit Test Run Configuration and exclude the optional OSGI jars.
Is there any way to exclude jars or modify the default classpath for an Eclipse Run Configuration?
If not, does anyone have a suggestion, how one should setup JUnit tests in such a case?
The only solution I was able to find is, to create a jar file from my unit tests and run the tests without OSGI from a different project with the test jar file on the classpath. But I would prefer a more elegant solution, ideally without the necessity of a second test project.
With the help of Gimbys comment, I was able to solve the problem. Although it is not possible in Eclipse to modify the default classpath generated by JDT, it is possible to entirely remove the default classpath and then add your own classpath in the classpath tab of the Runtime Configuration.
To remove the default classpath, one has to select the root entry that is named after the currently run project, and then click on the "remove" button. After that one can add all the jar files and projects that should be loaded in the classpath. The downside of this approach is, that jar files, that are normally provided by Eclipse plugins like e.g. junit.jar, must then also be selected manually (e.g. by adding it to a library folder of the project or by selecting the external jar in the plugins directory of the Eclipse installation folder).
I would suggest that you split up your project in 4 separate projects:
Logic: This package contains all the logic, so basically what your program/plug-in does
Logic.plugin: This Project resembles a Plugin-Project (OSGI-Bundle). This project contains only configurations needed for your plugin and everything dependent on osgi. It has a dependency on your Logic Project.
Logic.plugin.test: All the test cases for your osgi bundle. It is the normal plugin approach to split up logic and test cases
Logic.cli: This project contains the command line interface (or whatever your alternate application is). This also depends on the Logic project.
If your CLI project is only very small you may merge it with the Logic project.