This is an automated email from the ASF dual-hosted git repository. robertlazarski pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-core.git
commit 37322ed08f8b6874b77d7c993a24c3ea45fb1eb4 Author: Robert Lazarski <[email protected]> AuthorDate: Wed May 13 11:16:59 2026 -1000 Add embedded Tomcat support to axis2-spring-boot-starter The starter previously required WAR deployment because WarBasedAxisConfigurator depends on ServletContext.getRealPath() which returns null in embedded Tomcat. New axis2.repo property (application.properties or -D flag): - When set, overrides getRealPath() for both repository path and axis2.xml location - When empty (default), falls back to getRealPath() — no change to existing WAR deployments Changes: - Axis2Properties: new axis2.repo property with Javadoc - Axis2RepositoryAutoConfiguration: checks axis2.repo before getRealPath() for axis2.xml staging - Axis2ServletAutoConfiguration: checks axis2.repo before getRealPath() for repository path; also sets axis2.xml.path init-parameter (the root cause — embedded Tomcat's getResourceAsStream("/WEB-INF/conf/axis2.xml") returns null, causing WarBasedAxisConfigurator to load the minimal classpath default without JSON message builders) - Tests: 2 new tests for repo property (11 total, all pass) - spring-boot-starter.xml: updated header and deployment model to reflect embedded support Backward compatible: existing WAR deployments with axis2.repo unset continue to use getRealPath() as before. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --- .../apache/axis2/spring/boot/Axis2Properties.java | 24 +++++++++++++ .../boot/Axis2RepositoryAutoConfiguration.java | 11 ++++-- .../spring/boot/Axis2ServletAutoConfiguration.java | 41 ++++++++++++++++++++-- .../spring/boot/Axis2AutoConfigurationTest.java | 16 +++++++++ src/site/xdoc/docs/spring-boot-starter.xml | 29 ++++++++------- 5 files changed, 103 insertions(+), 18 deletions(-) diff --git a/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2Properties.java b/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2Properties.java index b7826c18ed..ff6765bb9c 100644 --- a/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2Properties.java +++ b/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2Properties.java @@ -82,6 +82,22 @@ public class Axis2Properties { */ private String configurationFile = ""; + /** + * Explicit path to the Axis2 repository (WEB-INF directory containing + * conf/axis2.xml, services/*.aar, and modules/*.mar). + * + * <p>When set, this overrides {@code ServletContext.getRealPath("/WEB-INF")} + * and enables embedded Tomcat support — embedded mode creates a temp + * docbase that lacks the Axis2 directory structure. + * + * <p>For embedded mode, point this to the exploded WAR from the build: + * <pre>axis2.repo=target/deploy/axis2-json-api/WEB-INF</pre> + * + * <p>For external container (WAR) deployment, leave this empty — + * the path is resolved automatically from the servlet context. + */ + private String repo = ""; + /** * OpenAPI and MCP sub-configuration. */ @@ -119,6 +135,14 @@ public class Axis2Properties { this.configurationFile = configurationFile; } + public String getRepo() { + return repo; + } + + public void setRepo(String repo) { + this.repo = repo; + } + public OpenApi getOpenapi() { return openapi; } diff --git a/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2RepositoryAutoConfiguration.java b/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2RepositoryAutoConfiguration.java index 1327df81a5..0dff77ca0b 100644 --- a/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2RepositoryAutoConfiguration.java +++ b/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2RepositoryAutoConfiguration.java @@ -80,10 +80,17 @@ public class Axis2RepositoryAutoConfiguration { } private void stageAxis2Config(ServletContext servletContext, Axis2Properties properties) { - String webInfPath = servletContext.getRealPath("/WEB-INF"); + // Priority: axis2.repo property > ServletContext.getRealPath("/WEB-INF") + // Embedded Tomcat returns null for getRealPath() because the temp + // docbase has no WEB-INF directory. The axis2.repo property points + // to the exploded WAR from the build. + String webInfPath = (properties.getRepo() != null && !properties.getRepo().isEmpty()) + ? properties.getRepo() + : servletContext.getRealPath("/WEB-INF"); if (webInfPath == null) { log.warn("Cannot resolve WEB-INF path — axis2.xml staging skipped. " - + "Ensure axis2.xml is pre-staged in WEB-INF/conf/"); + + "For embedded mode, set axis2.repo in application.properties " + + "or via -Daxis2.repo=target/deploy/.../WEB-INF"); return; } diff --git a/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2ServletAutoConfiguration.java b/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2ServletAutoConfiguration.java index 926e762391..c1243dfb44 100644 --- a/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2ServletAutoConfiguration.java +++ b/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2ServletAutoConfiguration.java @@ -66,15 +66,50 @@ public class Axis2ServletAutoConfiguration { axisServlet.setLoadOnStartup(1); // Set repository path — the critical init-parameter for WarBasedAxisConfigurator. + // Priority: axis2.repo property > ServletContext.getRealPath("/WEB-INF") // getRealPath() is called eagerly here to avoid WildFly VFS lazy-init issues. - String webInfPath = servletContext.getRealPath("/WEB-INF"); + // Embedded Tomcat returns null for getRealPath() — axis2.repo is required. + String webInfPath = (properties.getRepo() != null && !properties.getRepo().isEmpty()) + ? properties.getRepo() + : servletContext.getRealPath("/WEB-INF"); if (webInfPath != null) { axisServlet.setInitParameter( WarBasedAxisConfigurator.PARAM_AXIS2_REPOSITORY_PATH, webInfPath); log.info("axis2.repository.path = " + webInfPath); + + // Also set axis2.xml.path so WarBasedAxisConfigurator loads the + // correct axis2.xml (with JSON builders, enableJSONOnly, etc.) + // instead of falling back to the minimal classpath default. + // Critical for embedded Tomcat where servletContext.getResourceAsStream + // ("/WEB-INF/conf/axis2.xml") returns null. + java.io.File axis2xml = new java.io.File(webInfPath, "conf/axis2.xml"); + if (!axis2xml.isFile() && properties.getRepo() != null && !properties.getRepo().isEmpty()) { + // axis2.repo was explicitly set but axis2.xml is missing — fail fast + // instead of letting WarBasedAxisConfigurator silently load the minimal + // classpath default (which lacks JSON message builders). + throw new IllegalStateException( + "axis2.repo is set to " + webInfPath + + " but conf/axis2.xml does not exist at " + axis2xml + + ". Run 'mvn clean install' first to build the exploded WAR."); + } + if (axis2xml.isFile()) { + try { + axisServlet.setInitParameter( + WarBasedAxisConfigurator.PARAM_AXIS2_XML_PATH, + axis2xml.getCanonicalPath()); + log.info("axis2.xml.path = " + axis2xml.getCanonicalPath()); + } catch (java.io.IOException e) { + log.warn("Could not get canonical path for axis2.xml: " + + e.getMessage() + ". Falling back to absolute path."); + axisServlet.setInitParameter( + WarBasedAxisConfigurator.PARAM_AXIS2_XML_PATH, + axis2xml.getAbsolutePath()); + } + } } else { - log.warn("ServletContext.getRealPath(\"/WEB-INF\") returned null — " - + "AxisServlet will attempt to resolve the repository path itself"); + log.warn("Cannot resolve Axis2 repository path — " + + "for embedded mode, set axis2.repo in application.properties " + + "or via -Daxis2.repo=target/deploy/.../WEB-INF"); } // Map to configured path diff --git a/modules/spring-boot-starter/src/test/java/org/apache/axis2/spring/boot/Axis2AutoConfigurationTest.java b/modules/spring-boot-starter/src/test/java/org/apache/axis2/spring/boot/Axis2AutoConfigurationTest.java index e7e0cee3e0..d46b666a25 100644 --- a/modules/spring-boot-starter/src/test/java/org/apache/axis2/spring/boot/Axis2AutoConfigurationTest.java +++ b/modules/spring-boot-starter/src/test/java/org/apache/axis2/spring/boot/Axis2AutoConfigurationTest.java @@ -73,9 +73,25 @@ class Axis2AutoConfigurationTest { assertEquals("json", props.getMode(), "axis2.mode defaults to json"); assertEquals("/services", props.getServicesPath(), "axis2.services-path defaults to /services"); assertEquals("", props.getConfigurationFile(), "axis2.configuration-file defaults to empty"); + assertEquals("", props.getRepo(), "axis2.repo defaults to empty"); assertTrue(props.getOpenapi().isEnabled(), "axis2.openapi.enabled defaults to true"); } + @Test + void repoPropertyOverridesDefault() { + Axis2Properties props = new Axis2Properties(); + props.setRepo("target/deploy/myapp/WEB-INF"); + assertEquals("target/deploy/myapp/WEB-INF", props.getRepo()); + } + + @Test + void emptyRepoPropertyDoesNotOverride() { + Axis2Properties props = new Axis2Properties(); + // Empty string means "use ServletContext.getRealPath() as before" + assertTrue(props.getRepo().isEmpty(), + "Empty repo should not trigger the embedded mode path"); + } + @Test void soapModeSelectsSoapTemplate() { Axis2Properties props = new Axis2Properties(); diff --git a/src/site/xdoc/docs/spring-boot-starter.xml b/src/site/xdoc/docs/spring-boot-starter.xml index d54bc2c60f..d6666d4c9e 100644 --- a/src/site/xdoc/docs/spring-boot-starter.xml +++ b/src/site/xdoc/docs/spring-boot-starter.xml @@ -34,14 +34,14 @@ from a multi-day configuration project to a single Maven dependency. It handles servlet registration, repository configuration, and OpenAPI/MCP endpoint activation with sensible defaults — for both SOAP and JSON-RPC services.</p> -<p><strong>Important — WAR deployment only.</strong> This starter requires deployment -as a WAR file to an external servlet container (Tomcat 11, WildFly 32/39, or similar). -Spring Boot's embedded Tomcat mode (<code>java -jar</code>) is <strong>not -supported</strong> in this release. See <a href="#deployment_model">Deployment Model</a> -for details and rationale.</p> +<p>This starter supports both <strong>WAR deployment</strong> to an external container +(Tomcat 11, WildFly 32/39) and <strong>embedded Tomcat</strong> for local development. +For embedded mode, set <code>axis2.repo</code> in <code>application.properties</code> +to the exploded WAR directory from your build. +See <a href="#deployment_model">Deployment Model</a> for details.</p> <ul> -<li><a href="#deployment_model">0. Deployment Model (WAR Only)</a></li> +<li><a href="#deployment_model">0. Deployment Model (WAR + Embedded)</a></li> <li><a href="#quickstart">1. Quick Start</a></li> <li><a href="#how_axisservlet_works">2. How AxisServlet Works (SOAP vs JSON)</a></li> <li><a href="#properties">3. Configuration Properties</a></li> @@ -162,12 +162,13 @@ above 17.</p> <p>Other Jakarta EE 9+ containers (WebSphere Liberty, Payara) should work but are not tested.</p> -<h3>Will embedded mode be supported in the future?</h3> +<h3>Embedded mode limitations</h3> -<p>Embedded Tomcat support is planned for a future release. It requires a -<code>ClasspathBasedAxisConfigurator</code> that discovers services and modules -from the classpath rather than from <code>WEB-INF/</code>. Contributions -are welcome — see the +<p>The current embedded Tomcat support requires a pre-built exploded WAR +on the filesystem (via <code>axis2.repo</code>). A future improvement +would be a <code>ClasspathBasedAxisConfigurator</code> that discovers +services and modules directly from the classpath, eliminating the need +for a filesystem layout entirely. Contributions are welcome — see the <a href="https://github.com/apache/axis-axis2-java-core/blob/master/AXIS2_MODERNIZATION_PLAN.md">Axis2 Modernization Plan</a> for the full roadmap.</p> @@ -469,7 +470,8 @@ identical in structure to the configuration used by the legacy userguide <h3>Container matrix</h3> <p>See <a href="#deployment_model">Section 0</a> for the full container -compatibility matrix and the rationale for WAR-only deployment.</p> +compatibility matrix covering WAR deployment (Tomcat 11, WildFly 32/39) +and embedded Tomcat for local development.</p> <!-- ============================================================ --> <a name="http2"/> @@ -499,7 +501,8 @@ JSON message formatter pipeline:</p> with zero overhead when not used. See <a href="json-streaming-formatter.html#Field_Selection">Field Selection</a>.</li> <li><strong>C parity</strong> — the same streaming architecture exists - in Axis2/C via Apache httpd <code>mod_h2</code>. Both + in <a href="https://axis.apache.org/axis2/c/core/">Axis2/C</a> via + Apache httpd <code>mod_h2</code>. Both implementations use the same 64 KB flush interval, producing identical HTTP/2 DATA frame patterns.</li> </ul>
