major
#29789
tl:generate-java fails on the first build after a TopLogic version change: it neither resolves the engine web fragments nor reads the project's own model sources
Affects tl-maven-plugin 8.0.0-alpha10, goal generate-java (com.top_logic.build.maven.generate.GenerateJavaBinding).
Scenario
An application binds generate-java to generate-sources, as is common (and as recommended in the wrapper-generation documentation):
<plugin>
<groupId>com.top-logic</groupId>
<artifactId>tl-maven-plugin</artifactId>
<executions>
<execution>
<id>generate-binding</id>
<phase>generate-sources</phase>
<goals><goal>generate-java</goal></goals>
</execution>
</executions>
<configuration>
<modules>my.module</modules>
</configuration>
</plugin>
After raising the parent from 7.10.2 to 8.0.0-alpha10 (application version inherited from the parent), the very first build fails, so nothing can ever be installed:
- Failed to resolve dependency 'com.top-logic:<app>:jar:8.0.0-alpha10' ... Could not find artifact
- After bootstrapping the application's own artifact into the local repository, the goal fails with {{{
Generation for modules '...' failed. InvocationTargetException: Unable to createXMLProperties: Could not resolve '/WEB-INF/conf/top-logic.cluster.xml' to an existing resource. }}}
Analysis
The -X log of the goal shows for every engine module:
WARNING: No web fragment found for module '~/.m2/repository/com/top-logic/tl-core/8.0.0-alpha10/tl-core-8.0.0-alpha10.jar', searched: [~/.m2/repository/com/top-logic/tl-core/8.0.0-alpha10/tl-core-8.0.0-alpha10-web-fragment.war] ... INFO: Using resource paths: [file:~/.m2/repository/com/top-logic/<app>/8.0.0-alpha10/<app>-8.0.0-alpha10-web-fragment.war]
- The goal sets up the application configuration from web fragments in the local repository only: the application's own *installed* -web-fragment.war and the fragments PathInfo expects next to each engine jar.
- It never asks Maven to resolve the web-fragment classifier artifacts. With a new engine version none of them is in the local repository yet (they are only fetched later in a normal build), so the configuration is incomplete and loading metaConf.txt fails.
- It does not read the model from the project's src/main/webapp either. Therefore it needs the project's own artifact in the target version, which does not exist before the first successful build, and after a model change the generator sees the stale model until the project has been installed again.
Pre-fetching the fragments (mvn dependency:copy-dependencies -Dclassifier=web-fragment -Dtype=war -DfailOnMissingClassifierArtifact=false) and installing the project once with the generation skipped makes it work, confirming the analysis.
Related: #29705 fixed finding a fragment located in another repository of a maven.repo.local.tail chain; here the fragments are never fetched.
Expected
- generate-java resolves the web-fragment wars of the project dependencies through Maven artifact resolution (type=war, classifier=web-fragment, missing ones skipped), like any other dependency.
- It uses the project's own src/main/webapp (the reactor sources) instead of the project's installed artifact.
- Then the goal works in generate-sources with an empty local repository, after a version change, and directly after a model change.
Workaround
Do not bind generate-java to the lifecycle; after mvn install, run mvn tl:generate-java on demand when the model changes.
Lösung
- AppModuleStack (tl-maven-plugin) determines the TopLogic modules of an application: each jar dependency that is a TopLogic web component, ordered along the module dependencies, with its -web-fragment.war resolved through Maven artifact resolution. It provides the resource stack of the application (highest priority first) and closes the opened archives afterwards. The goals app and generate-java both use it.
- Goal app: unchanged behaviour (the app layer is the application's resolved web fragment, an unresolvable fragment is an error); the built war is identical.
- Goal generate-java:
- The application layer is the project's own src/main/webapp, so the generator always sees the current model sources.
- The class path contains the project's target/classes (if present) instead of the project's installed artifact, so the goal never needs the project's own artifact.
- A web fragment that cannot be resolved is skipped with the warning Skipping web fragment of ....
- The resource stack is logged (Resource stack (highest priority first):).
- WrapperGenerator.generate(List<Path>, String[]) (tl-element) generates from an explicitly given resource stack (first entry overrides later ones) instead of deriving it from the class path; generate-java calls it.
- The engine parent POM tl-parent-all no longer declares the snapshot repository tl-snapshots (https://dev.top-logic.com/nexus/repository/toplogic-snapshots/) as repository and plugin repository: a build of the engine never takes TopLogic snapshots from a remote, only from its own reactor and local repository. The snapshotRepository of the distributionManagement (deployment) is unchanged.
Tests
- TestWrapperGenerator (tl-element): generation through the explicit resource stack, and the error for a module missing in the stack.
- TestAppModuleStack (tl-maven-plugin): build order, resource-stack order, modules without fragment, closing the archives.
- TestGenerateJavaIntegration (test module test.com.top_logic.build.maven, a regular module test, so the CI runs it): builds the test application src/test/projects/generate-java-it (never installed, generate-java bound to generate-sources) up to test-compile in a separate Maven process. The build starts with an empty local repository and gets the plugin and the other TopLogic artifacts from the local repository of the running build, which the test adds as remote repository through global settings; the user settings of the running build (mirrors such as the Central proxy, proxies, credentials) apply unchanged. The test module depends on tl-maven-plugin and tl-element, so that the reactor has installed them before the test runs (tl-maven-plugin itself cannot depend on the engine, whose build uses the plugin). The test checks that the binding is generated and compiled and that all engine fragments were resolved into the empty repository. With the previous goal implementation it fails with the ticket's first symptom.
- The invoker profile run-its of tl-maven-plugin (never run by the CI) and its trivial IT are removed.
Not covered
- The generator still loads the complete application configuration, so configuration referencing the application's own classes needs these classes compiled (target/classes) when the goal runs. This is left to a separate ticket.
Migration
An application whose POM inherits from the TopLogic parent POMs and builds against a TopLogic SNAPSHOT version from the TopLogic Nexus must declare the snapshot repository itself, since tl-parent-all no longer does. Applications created from tl-archetype-app already declare it; others add to their pom.xml:
{{{#!xml <repositories>
<repository>
<id>tl-snapshots</id>
<url>https://dev.top-logic.com/nexus/repository/toplogic-snapshots/</url>
<releases><enabled>false</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories> <pluginRepositories>
<pluginRepository>
<id>tl-snapshots</id>
<url>https://dev.top-logic.com/nexus/repository/toplogic-snapshots/</url>
<releases><enabled>false</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</pluginRepository>
</pluginRepositories> }}}
Applications on a released TopLogic version need no change.