-
Notifications
You must be signed in to change notification settings - Fork 7
Wars sharedclasspath #617
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
jonenst
wants to merge
7
commits into
main
Choose a base branch
from
wars_sharedclasspath
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Wars sharedclasspath #617
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| SHOULD_INIT_GEO_DATA=true | ||
| SHOULD_INIT_LINES_CATALOG=true | ||
|
|
||
| COMPOSE_PROJECT_NAME=gridstudy | ||
|
|
||
| TOMCAT_PORT=12345 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| # WAR Deploy Tool | ||
|
|
||
| Generated with little supervision by opus4.6. | ||
|
|
||
| Builds GridSuite microservices as WAR files for docker-compose deployment, **without modifying any original source files**. | ||
|
|
||
| ## How it works | ||
|
|
||
| Instead of patching server sources, this tool generates **wrapper Maven projects** that: | ||
| 1. Depend on the original server JAR (classes artifact) | ||
| 2. Add a generated `SpringBootServletInitializer` class that delegates to the original Application class | ||
| 3. Package everything as a WAR with a clean context-path name | ||
|
|
||
| ``` | ||
| Original server (unmodified) Wrapper project (generated) | ||
| ┌─────────────────────────┐ ┌─────────────────────────┐ | ||
| │ report-server/ │ │ war-wrappers/report-server/ | ||
| │ pom.xml (jar) │◄─────────│ pom.xml (war) │ | ||
| │ ReportApplication.java│ │ WarInitializer.java │ | ||
| └─────────────────────────┘ └─────────────────────────┘ | ||
| │ │ | ||
| ▼ ▼ | ||
| gridsuite-report-server.jar report-server.war | ||
| ``` | ||
|
|
||
| ## Quick Start | ||
|
|
||
| ```bash | ||
| # Build all WARs and prepare for docker-compose | ||
| ./wars.sh | ||
|
|
||
| # Start the stack | ||
| docker compose up | ||
| ``` | ||
|
|
||
| ## Enabling/disabling servers | ||
|
|
||
| Edit the `MANIFEST` array in `war-deploy.sh`. Comment lines with `#` to disable: | ||
|
|
||
| ```bash | ||
| "actions-server|actions-server||..." # enabled | ||
| #"timeseries-server|timeseries-server||..." # disabled | ||
| ``` | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - Java 21+ | ||
| - Maven 3.8+ | ||
| - Server submodules checked out and buildable in the layout of Gridsuite Aggregator | ||
|
|
||
| ## Excluded (Boot WebFlux — incompatible(??) with WAR) | ||
|
|
||
| - config-server | ||
| - config-notification-server | ||
| - directory-notification-server | ||
| - merge-notification-server | ||
| - study-notification-server | ||
| - gateway | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| Shared Classloading Issues — WAR Deployment on Single Tomcat | ||
| ============================================================= | ||
|
|
||
| All 21 Spring Boot microservices deployed as WARs on one Tomcat instance, | ||
| with all dependency JARs in Tomcat's shared lib/ (common classloader). | ||
| Each WAR contains only its own server classes JAR. | ||
|
|
||
| ISSUE 1: Bean definition conflicts | ||
| ─────────────────────────────────── | ||
| Symptom: BeanDefinitionOverrideException — duplicate bean names from | ||
| @Configuration classes in gridsuite-computation, gridsuite-network-modification | ||
| JARs now visible to ALL webapps via common classloader. | ||
| Fix: -Dspring.main.allow-bean-definition-overriding=true in CATALINA_OPTS. | ||
|
|
||
| ISSUE 2: DataSource auto-configuration on non-DB servers | ||
| ──────────────────────────────────────────────────────── | ||
| Symptom: "Could not resolve placeholder 'powsybl-ws.database.name'" on | ||
| explore-server, network-conversion-server, network-map-server. | ||
| Cause: powsybl-ws-commons JAR in shared lib has application.yaml referencing | ||
| DB properties. With HikariCP/spring-jdbc on common classpath, | ||
| DataSourceAutoConfiguration activates for all webapps. | ||
| Fix: Auto-detect non-DB servers (no JPA/JDBC/Liquibase deps) and add | ||
| spring.autoconfigure.exclude entries to their externalized configs: | ||
| DataSourceAutoConfiguration, HibernateJpaAutoConfiguration, | ||
| LiquibaseAutoConfiguration, DataSourceTransactionManagerAutoConfiguration. | ||
|
|
||
| ISSUE 3: Spring Security activating on all servers | ||
| ────────────────────────────────────────────────── | ||
| Symptom: All servers return "Please sign in" login page. | ||
| Cause: Only explore-server depends on spring-security, but security JARs | ||
| in shared lib activate SecurityAutoConfiguration for all webapps. | ||
| Fix: Auto-detect non-security servers (no spring-boot-starter-security or | ||
| spring-security-config in deps) and add exclusions: | ||
| SecurityAutoConfiguration, UserDetailsServiceAutoConfiguration, | ||
| ManagementWebSecurityAutoConfiguration. | ||
|
|
||
| ISSUE 4: Logback ConcurrentModificationException during parallel deployment | ||
| ─────────────────────────────────────────────────────────────────────────── | ||
| Symptom: ConcurrentModificationException in ContextBase.getCopyOfPropertyMap | ||
| during startup. | ||
| Cause: startStopThreads="10" in server.xml → 10 WARs deploy simultaneously, | ||
| all sharing the same Logback LoggerContext from common classloader. | ||
| Race condition in property map access. | ||
| Fix: Set startStopThreads="1" in server.xml for sequential deployment. | ||
| Shared lib preloads all classes, so sequential is fast enough. | ||
|
|
||
| ISSUE 5: ServiceLoader "not a subtype" errors | ||
| ────────────────────────────────────────────── | ||
| Symptom: ServiceConfigurationError: ActivePowerControlLoader not a subtype | ||
| of ExtensionLoader. | ||
| Cause: Version mismatch between shared lib (e.g. 1.40.0) and per-WAR | ||
| override JARs (1.41.0). Different classloaders load the same | ||
| interface → different type identity → ServiceLoader rejects impl. | ||
| Fix: Removed per-WAR version overrides entirely. All servers use the | ||
| highest version from shared lib (sort -k5Vr). No override JARs in | ||
| WEB-INF/lib/ — only the server's own classes JAR. | ||
|
|
||
| ISSUE 6: antlr4 tool JAR conflicts with antlr4-runtime | ||
| ─────────────────────────────────────────────────────── | ||
| Symptom: ATN version mismatch errors from Hibernate HQL parser. | ||
| Cause: org.antlr:antlr4:4.5.1 (parser generator tool) bundles old runtime | ||
| classes that conflict with antlr4-runtime:4.13.0 used by Hibernate. | ||
| Fix: Exclude org.antlr:antlr4: (tool JAR) from shared lib via grep filter. | ||
| antlr4-runtime (the actual dependency) is kept. | ||
|
|
||
| ISSUE 7: ResourceBundle not found for reports.properties | ||
| ──────────────────────────────────────────────────────── | ||
| Symptom: MissingResourceException: Can't find bundle for base name | ||
| com.powsybl.network.conversion.server.reports. | ||
| Cause: MultiBundleMessageTemplateProvider (in powsybl-commons, common CL) | ||
| calls ResourceBundle.getBundle() without explicit classloader → | ||
| uses caller's CL (common CL) → can't see .properties files inside | ||
| each WAR's server JAR in WEB-INF/lib/. | ||
| Fix: Extract reports*.properties from all 9 server source trees, package | ||
| into a single resource-bundles.jar in shared lib. All bundle paths | ||
| are unique (different packages), so no conflicts. | ||
|
|
||
| ISSUE 8: Elasticsearch thread pool explosion (441 threads) | ||
| ────────────────────────────────────────────────────────── | ||
| Symptom: 706 threads total; 441 are elasticsearch-rest-client I/O threads. | ||
| 21 ES client pools × 21 threads each (one per CPU core). | ||
| Cause: ElasticsearchRestClientAutoConfiguration activates for ALL 21 webapps | ||
| because ES client JARs are on the common classpath. Only 5 servers | ||
| actually use Elasticsearch (case, directory, network-conversion, | ||
| network-modification, study). | ||
| Fix: Auto-detect non-ES servers (no elasticsearch/elastic-clients in deps) | ||
| and add exclusions: ElasticsearchRestClientAutoConfiguration, | ||
| ElasticsearchClientAutoConfiguration, | ||
| ReactiveElasticsearchClientAutoConfiguration, | ||
| ElasticsearchDataAutoConfiguration, | ||
| ElasticsearchRepositoriesAutoConfiguration. | ||
| Result: 706 → 456 threads (−250). | ||
|
|
||
| ISSUE 9: OpenTelemetry tracing auto-configuration failure | ||
| ───────────────────────────────────────────────────────── | ||
| Symptom: APPLICATION FAILED TO START — OpenTelemetryTracingAutoConfiguration | ||
| required a bean of type 'io.opentelemetry.api.OpenTelemetry'. | ||
| Cause: OTel JARs on common classpath activate tracing autoconfigs for all | ||
| webapps. Each webapp also creates BatchSpanProcessor and | ||
| BatchLogRecordProcessor threads (42 total) needlessly. | ||
| Fix: Exclude for all servers (none configure OTel tracing): | ||
| OpenTelemetryAutoConfiguration (×2 in different packages), | ||
| OpenTelemetryTracingAutoConfiguration, | ||
| OtlpAutoConfiguration, OtlpTracingAutoConfiguration, | ||
| OpenTelemetryLoggingAutoConfiguration. | ||
|
|
||
| MEMORY USAGE REPORT (21 webapps, shared classloading) | ||
| ====================================================== | ||
| Container: 2.5g limit, no swap | JVM: -Xmx1g, G1GC | ||
|
|
||
| Docker stats: 2.24 GiB / 2.5 GiB (89%) — 488 threads (PIDs) | ||
|
|
||
| NMT committed breakdown (1,619 MB total): | ||
| Java Heap 1,020 MB (Xmx 1g, ~300MB used at idle, ~1g under load) | ||
| Metaspace 193 MB (43K classes across 21 webapps) | ||
| GC (G1) 92 MB (card tables, remembered sets) | ||
| Code cache 85 MB (JIT compiled code) | ||
| Symbol table 71 MB (interned strings, 582K entries) | ||
| Threads 51 MB (488 threads × ~1MB stack) | ||
| Class metadata 34 MB (class pointers, vtables) | ||
| NMT overhead 14 MB | ||
| Other (CDS, etc.) 59 MB | ||
|
|
||
| RSS vs NMT gap (~600 MB): | ||
| NMT committed = 1,619 MB, docker reports 2,236 MB. | ||
| Difference is thread stack RSS, glibc malloc arenas, mmap'd JARs, | ||
| and kernel page table overhead not tracked by NMT. | ||
|
|
||
| Comparison vs 21 separate containers: | ||
| Each server standalone uses ~300-500 MB (avg ~400 MB). | ||
| 21 × 400 MB = 8.4 GB total. | ||
| Shared classloading: 2.24 GB = ~73% memory reduction. | ||
|
|
||
| ISSUE 10: Maven build failures | ||
| ───────────────────────────── | ||
| 8a) Thread contention: -T2.0C (40 threads on 20 cores) caused Maven lock | ||
| contention. Fixed: -T4. | ||
| 8b) Missing artifact com.bea.xml:jsr173-ri:1.0 (transitive of stax-utils). | ||
| Fixed: wildcard <exclusions> on all shared-deps POM entries since | ||
| _shared.txt is already the full transitive closure. | ||
| 8c) Empty WARs: packagingExcludes=WEB-INF/lib/*.jar removed everything. | ||
| maven-war-plugin doesn't support ! negation in packagingExcludes. | ||
| Fixed: switched to packagingIncludes (whitelist approach). | ||
|
Comment on lines
+134
to
+143
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ISSUE numbering mismatch. The header says "ISSUE 10" but the sub-items below are labeled 🔧 Proposed fix ISSUE 10: Maven build failures
─────────────────────────────
-8a) Thread contention: -T2.0C (40 threads on 20 cores) caused Maven lock
+10a) Thread contention: -T2.0C (40 threads on 20 cores) caused Maven lock
contention. Fixed: -T4.
-8b) Missing artifact com.bea.xml:jsr173-ri:1.0 (transitive of stax-utils).
+10b) Missing artifact com.bea.xml:jsr173-ri:1.0 (transitive of stax-utils).
Fixed: wildcard <exclusions> on all shared-deps POM entries since
_shared.txt is already the full transitive closure.
-8c) Empty WARs: packagingExcludes=WEB-INF/lib/*.jar removed everything.
+10c) Empty WARs: packagingExcludes=WEB-INF/lib/*.jar removed everything.
maven-war-plugin doesn't support ! negation in packagingExcludes.
Fixed: switched to packagingIncludes (whitelist approach).🤖 Prompt for AI Agents |
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: gridsuite/deployment
Length of output: 121
🏁 Script executed:
Repository: gridsuite/deployment
Length of output: 1210
🏁 Script executed:
# Check the main directory structure ls -laRepository: gridsuite/deployment
Length of output: 713
🏁 Script executed:
Repository: gridsuite/deployment
Length of output: 181
🏁 Script executed:
# Read the multiwar README to understand context cat docker-compose/multiwar/README.mdRepository: gridsuite/deployment
Length of output: 1830
🏁 Script executed:
Repository: gridsuite/deployment
Length of output: 92
🌐 Web query:
Spring Boot WebFlux WAR packaging compatibility servlet container💡 Result:
Spring Boot WebFlux applications are not compatible with WAR packaging for deployment to servlet containers. Official Spring Boot documentation explicitly states: "Because Spring WebFlux does not strictly depend on the Servlet API and applications are deployed by default on an embedded Reactor Netty server, War deployment is not supported for WebFlux applications."[1][2][3][4]. This position is consistent across versions, including recent 3.3.x documentation[2][4], and Spring Boot team members have confirmed no plans to support it due to technical challenges like requiring Servlet 3.1+ with async I/O and risks of mixing blocking/non-blocking code[5][6]. Spring Boot recommends packaging WebFlux apps as executable JARs with embedded Netty[5][6]. While plain Spring Framework WebFlux supports WAR deployment via AbstractReactiveWebInitializer on Servlet 3.1+ containers[7][8][9], this is not integrated with Spring Boot's auto-configuration and leads to errors or unsupported behavior when attempted[5][10][11][12]. No changes noted as of 2026[13][14].
Citations:
🌐 Web query:
Spring Cloud Config Server WebFlux Spring Cloud Gateway architecture💡 Result:
Spring Cloud Config Server WebFlux + Spring Cloud Gateway: how the architecture typically fits together (and where WebFlux can be tricky). 1) What each component is (and what protocol it speaks) - Spring Cloud Config Server exposes an HTTP resource-based API for external configuration (name/value pairs or YAML) and is embeddable in a Spring Boot app using
@EnableConfigServer. It runs as a Spring Boot application (default port 8080; commonly 8888). [1] - Spring Cloud Gateway Server WebFlux is a reactive (Spring WebFlux/Project Reactor) gateway; its configuration (routes, predicates, filters) is driven by RouteDefinitionLocator/RouteDefinition and routes are matched via Spring WebFlux HandlerMapping infrastructure. [2][3][4] 2) Canonical “centralized config + reactive gateway” architecture A typical setup is: - Config Server (Spring Boot app): backs centralized config and serves it over HTTP. - Gateway (Spring Cloud Gateway on WebFlux): routes incoming client requests to downstream services (often discovered via Eureka, though that’s a separate concern). Gateway is itself reactive and expects WebFlux runtime semantics. [2][4][3] - Each downstream microservice (including Gateway itself if desired) is a Config Client: it fetches its own configuration from Config Server at startup using the Config Client support (connects to Config Server via spring.cloud.config.uri or spring.config.import=optional:configserver:...). [5][6] Key idea: Config Server is not “wired into” Gateway request routing at the WebFlux layer; instead, it provides configuration to the apps that need it (Gateway and downstream services). Config Server is an HTTP backend for configuration, not a routing component for Gateway. [1][5] 3) Where “Config Server + WebFlux” can be confusing - If you add Spring WebFlux starter(s) to Config Server, you may run into servlet-vs-reactive controller differences. A Stack Overflow report notes that the Config Server ResourceController can rely on ServletWebRequest, and therefore may not work properly with a reactive stack out of the box. [7] So for production-grade architectures, treat Config Server as a server that provides an HTTP API for config, and ensure its own web stack/controller compatibility matches the Spring Cloud Config version you’re using (don’t assume that adding spring-boot-starter-webflux will automatically make every endpoint reactive). [7][1] 4) How Gateway configuration relates to Config Server - Spring Cloud Gateway’s route configuration is driven by route definitions (RouteDefinitionLocator) and can be provided via properties (default PropertiesRouteDefinitionLocator loads from Spring Boot@ConfigurationProperties). [3] - Therefore, the most common approach is: a) Put Gateway route settings (e.g., spring.cloud.gateway.routes.* or equivalent RouteLocator configuration) in the config repository. b) Have the Gateway application fetch those settings from Config Server as part of its normal Config Client startup. This keeps Gateway reactive (it still runs on WebFlux) while config is supplied via the Config Server’s HTTP API. [3][1][5] 5) A concrete “wiring” pattern (conceptual) - Config Server: serves configuration artifacts for each application name/profile over HTTP. [1] - Gateway app: starts, connects to Config Server, loads properties (including Gateway route/predicate/filter config), then starts routing requests reactively. [5][3][4] If you tell me your exact versions (Spring Boot + Spring Cloud release train + Spring Cloud Config/Gateway versions) and whether you want Gateway routes to come from Config Server Git files or from discovery/database, I can draft an example application.yml/bootstrapping layout for both Config Server and Gateway.Citations:
Remove the uncertainty markers from the WebFlux incompatibility statement.
The exclusion is correct and well-documented: Spring Boot officially does not support WAR packaging for WebFlux applications because WebFlux depends on an embedded Reactor Netty server and does not integrate with the Servlet API. Replace the "(??) " with a definitive statement since this is a documented limitation, not an uncertain one.
Consider updating to: "Excluded (Spring Boot WebFlux — not compatible with WAR packaging)"
🤖 Prompt for AI Agents