War plugin causes package to be accessible from more than one module

Here is a setup that causes this problem (though there might be a simpler one):
Create a parent project parent with settings.gradle:

rootProject.name = "parent"
include "api", "provider", "consumer"

Then put these 3 subprojects inside it:
test.zip (660.6 KB)

provider and api are modular, consumer is not.

Both provider and consumer depend on api, and provider also exports api.
consumer depends on provider in its tests only.

Without the war plugin of consumer, the project compiles fine in Eclipse. However, the war plugin creates a Web App Libraries container with api in it, casing an error:
The package com.api is accessible from more than one module: <unnamed>, api
because api is available in 2 places (is what I understand).

Maybe there’s something wrong with the Web App Libraries configuration?

I’m not sure if this is a bug in Buildship or in Eclipse. I tested on 4.32 and a recent nightly build of 4.35. Gradle 8.10.2 or 8.11.1.

@oleosterhagen Sorry for pinging you directly, but you usually have the answers for these :slight_smile:

Sorry for the late reply. I first had to understand the bigger picture:

  • The Web App Libraries classpath container from Eclipse WTP puts all entries on the classpath never on the modulepath. The container entries are created in FlexibleProjectContainer without the module attribute.
  • The Project and External Dependencies classpath container from Buildship is appended - when missing - after any other classpath containers. So normally the container from WTP is ordered before the container from Buildship (see Java Build Path > Order and Export in the project properties).
  • api is not modular in the WTP container.
  • api and provider are modular in the Buildship container.
  • api is initially resolved by the WTP container and so its packages are associated with the unnamed module.
  • provider is resolved by the Buildship container and so its packages are associated with the named module model.

While reading the module descriptor for model (in project provider) the requires clause for module apihas to be resolved. This time the module is found on the modulepath and now the packages from api are accessible through the unnamed module and api at the same time. (Name lookup in JDT seems here to be globally and not scoped to a single project.)

Because javac behaves differently, an option in JDT has been introduced to ignore this specific error (PR #424). Unfortunately, there is no option in the UI for this. But it can be manually added to consumer/.settings/org.eclipse.jdt.core.prefs:

org.eclipse.jdt.core.compiler.ignoreUnnamedModuleForSplitPackage=enabled

After Project > Clean… the error disappears for me. I do not know, if this works in every scenario.

Another possible workaround could be to change the order of the classpath containers. When api is found on the container from Buildship before the container from WTP, it should be put on the modulepath and its packages do not end in the unnamed module.

// consumer/build.gradle

plugins {
    // [...]
    id("eclipse")
}

eclipse {
    classpath {
        containers -= 'org.eclipse.jst.j2ee.internal.web.container'
        containers 'org.eclipse.buildship.core.gradleclasspathcontainer', 'org.eclipse.jst.j2ee.internal.web.container'
    }
}

Presumably the right solution would be changes to Eclipse WTP. I haven’t found any discussion on this yet.

Some interesting links:

1 Like

Thank you for the detailed response. I just quickly tested by changing the order of exports in Eclipse to
image
and I can confirm that the issue is resolved. Of course, this is temporary and I will need to try the more permanent solution.

I would like to understand, however, why there is the additional Web App Libraries container to begin with. It seems like an implementation detail of WTP that the user shouldn’t concern themselves with.

The Web App Libraries container adds project and library dependencies from the deployment assembly to the project classpath.

When you have a project which is not managed by Gradle or Maven you can even remove these project and library dependencies from the Java Build Path and they can still be resolved through the Web App Libraries container.

There may be other ways to implement the deployment functionality of Eclipse WTP. So, yes - this container can be seen as an implementation detail.

1 Like

The solution with

works!

Where do I need to file this bug (if it wasn’t already)?