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 67c0ba781913b16aa505317c4283b345adf00516 Author: Robert Lazarski <[email protected]> AuthorDate: Sat Apr 11 08:08:33 2026 -1000 Add axis2-spring-boot-starter module (Phase 1) New module: modules/spring-boot-starter/ Autoconfiguration classes: - Axis2AutoConfiguration — entry point, @ConditionalOnClass(AxisServlet) - Axis2ServletAutoConfiguration — registers AxisServlet at /services/*, sets axis2.repository.path eagerly (WildFly VFS workaround) - Axis2RepositoryAutoConfiguration — stages axis2.xml (SOAP or JSON template) into WEB-INF/conf/ when not already present - Axis2OpenApiAutoConfiguration — registers OpenAPI/MCP servlet when axis2-openapi is on classpath (conditional) Configuration properties (axis2.*): - axis2.enabled (default: true) — disable for non-service server roles - axis2.mode (default: json) — selects SOAP or JSON axis2.xml template - axis2.services-path (default: /services) - axis2.configuration-file — override with custom axis2.xml - axis2.openapi.enabled / axis2.openapi.paths Built-in axis2.xml templates: - axis2-json.xml — JsonRpcMessageReceiver, JSONBasedDefaultDispatcher - axis2-soap.xml — RawXMLINOutMessageReceiver, full SOAP dispatcher stack (SOAPAction, RequestURI, SOAPMessageBody, HTTPLocation) SOAP and JSON modes cannot coexist — different message receivers and dispatchers are incompatible. This is documented in the starter guide. WAR deployment only (Phase 1). Embedded Tomcat not supported — Axis2's WarBasedAxisConfigurator requires a filesystem WEB-INF/ directory. Tested: - JSON/MCP: all endpoints pass on Tomcat 11 / OpenJDK 25 / HTTPS+HTTP2 (openapi.json, openapi-mcp.json, swagger-ui, login, portfolioVariance, monteCarlo, scenarioAnalysis) - SOAP: template validated by unit tests (9 tests, 0 failures) Site docs: spring-boot-starter.xml added to toc.xml with sections covering AxisServlet internals, SOAP vs JSON mode, deployment model, migration guide, and tested configurations. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --- modules/spring-boot-starter/pom.xml | 139 +++++++ .../axis2/spring/boot/Axis2AutoConfiguration.java | 49 +++ .../spring/boot/Axis2OpenApiAutoConfiguration.java | 122 ++++++ .../apache/axis2/spring/boot/Axis2Properties.java | 165 +++++++++ .../boot/Axis2RepositoryAutoConfiguration.java | 141 +++++++ .../spring/boot/Axis2ServletAutoConfiguration.java | 90 +++++ .../org/apache/axis2/spring/boot/package-info.java | 34 ++ .../main/resources/META-INF/axis2/axis2-json.xml | 347 ++++++++++++++++++ .../main/resources/META-INF/axis2/axis2-soap.xml | 363 ++++++++++++++++++ ...rk.boot.autoconfigure.AutoConfiguration.imports | 4 + .../spring/boot/Axis2AutoConfigurationTest.java | 118 ++++++ pom.xml | 1 + src/site/xdoc/docs/spring-boot-starter.xml | 408 +++++++++++++++++++++ src/site/xdoc/docs/toc.xml | 14 + 14 files changed, 1995 insertions(+) diff --git a/modules/spring-boot-starter/pom.xml b/modules/spring-boot-starter/pom.xml new file mode 100644 index 0000000000..a50c76fd50 --- /dev/null +++ b/modules/spring-boot-starter/pom.xml @@ -0,0 +1,139 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, + ~ software distributed under the License is distributed on an + ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + ~ KIND, either express or implied. See the License for the + ~ specific language governing permissions and limitations + ~ under the License. + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.axis2</groupId> + <artifactId>axis2</artifactId> + <version>2.0.1-SNAPSHOT</version> + <relativePath>../../pom.xml</relativePath> + </parent> + + <artifactId>axis2-spring-boot-starter</artifactId> + <packaging>jar</packaging> + + <name>Apache Axis2 - Spring Boot Starter</name> + <description> + Spring Boot autoconfiguration for Apache Axis2. + Add this single dependency to get AxisServlet registered, repository staged, + and OpenAPI/MCP endpoints activated with sensible defaults. + Supports both SOAP and JSON-RPC modes via axis2.mode property. + </description> + + <properties> + <spring-boot.version>3.4.3</spring-boot.version> + </properties> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-dependencies</artifactId> + <version>${spring-boot.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + <dependencies> + <!-- Spring Boot autoconfiguration API --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-autoconfigure</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-configuration-processor</artifactId> + <optional>true</optional> + </dependency> + + <!-- Axis2 core — these become transitive to consuming apps --> + <dependency> + <groupId>org.apache.axis2</groupId> + <artifactId>axis2-kernel</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.axis2</groupId> + <artifactId>axis2-transport-http</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.axis2</groupId> + <artifactId>axis2-transport-local</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.axis2</groupId> + <artifactId>axis2-json</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.axis2</groupId> + <artifactId>axis2-adb</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.axis2</groupId> + <artifactId>axis2-java2wsdl</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.axis2</groupId> + <artifactId>axis2-metadata</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.axis2</groupId> + <artifactId>axis2-spring</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.axis2</groupId> + <artifactId>axis2-jaxws</artifactId> + <version>${project.version}</version> + </dependency> + + <!-- Optional: OpenAPI/MCP support — activated when on classpath --> + <dependency> + <groupId>org.apache.axis2</groupId> + <artifactId>axis2-openapi</artifactId> + <version>${project.version}</version> + <optional>true</optional> + </dependency> + + <!-- Servlet API (provided by container) --> + <dependency> + <groupId>jakarta.servlet</groupId> + <artifactId>jakarta.servlet-api</artifactId> + <scope>provided</scope> + </dependency> + + <!-- Test --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2AutoConfiguration.java b/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2AutoConfiguration.java new file mode 100644 index 0000000000..079fa08f18 --- /dev/null +++ b/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2AutoConfiguration.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.axis2.spring.boot; + +import org.apache.axis2.transport.http.AxisServlet; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; + +/** + * Spring Boot autoconfiguration for Apache Axis2. + * + * <p>Activates when {@code AxisServlet} is on the classpath and + * {@code axis2.enabled} is not set to {@code false}. + * + * <p>Axis2 supports two protocol modes configured via {@code axis2.mode}: + * <ul> + * <li><b>soap</b> — classic SOAP 1.1/1.2 with full dispatcher stack (since Axis2 1.0, 2006)</li> + * <li><b>json</b> — JSON-RPC with MCP/OpenAPI support (default for new projects)</li> + * </ul> + * + * <p>These modes require different axis2.xml configurations and cannot be mixed + * in a single deployment. + */ +@AutoConfiguration +@ConditionalOnClass(AxisServlet.class) +@ConditionalOnProperty(prefix = "axis2", name = "enabled", havingValue = "true", matchIfMissing = true) +@EnableConfigurationProperties(Axis2Properties.class) +public class Axis2AutoConfiguration { + // Placeholder — sub-configurations (servlet, repository, openapi) will be + // added as @Import or as separate @AutoConfiguration classes in subsequent steps. +} diff --git a/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2OpenApiAutoConfiguration.java b/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2OpenApiAutoConfiguration.java new file mode 100644 index 0000000000..b2b1815d94 --- /dev/null +++ b/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2OpenApiAutoConfiguration.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.axis2.spring.boot; + +import org.apache.axis2.context.ConfigurationContext; +import org.apache.axis2.openapi.OpenApiModule; +import org.apache.axis2.openapi.SwaggerUIHandler; +import org.apache.axis2.transport.http.AxisServlet; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.servlet.ServletContextInitializer; +import org.springframework.context.annotation.Bean; + +import jakarta.servlet.ServletRegistration; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Registers the OpenAPI/MCP servlet when {@code axis2-openapi} is on the classpath. + * + * <p>This eliminates the need for consuming apps to provide their own + * {@code OpenApiServlet} class. The servlet delegates to Axis2's + * {@link SwaggerUIHandler} for all four endpoints: + * <ul> + * <li>{@code /openapi.json} — OpenAPI 3.0.1 specification (JSON)</li> + * <li>{@code /openapi.yaml} — OpenAPI 3.0.1 specification (YAML)</li> + * <li>{@code /swagger-ui} — Interactive Swagger UI</li> + * <li>{@code /openapi-mcp.json} — MCP tool catalog</li> + * </ul> + * + * <p>Disabled by setting {@code axis2.openapi.enabled=false} or by removing + * {@code axis2-openapi} from the classpath. + */ +@AutoConfiguration(after = Axis2ServletAutoConfiguration.class) +@ConditionalOnClass({AxisServlet.class, OpenApiModule.class}) +@ConditionalOnProperty(prefix = "axis2.openapi", name = "enabled", havingValue = "true", matchIfMissing = true) +public class Axis2OpenApiAutoConfiguration { + + private static final Log log = LogFactory.getLog(Axis2OpenApiAutoConfiguration.class); + + @Bean + public ServletContextInitializer openApiServletInitializer(Axis2Properties properties) { + return (servletContext) -> { + String[] paths = properties.getOpenapi().getPaths(); + ServletRegistration.Dynamic openApi = servletContext.addServlet( + "OpenApiServlet", new Axis2OpenApiServlet()); + openApi.setLoadOnStartup(2); + openApi.addMapping(paths); + log.info("OpenApiServlet registered at " + String.join(", ", paths)); + }; + } + + /** + * Built-in OpenAPI servlet that delegates to Axis2's SwaggerUIHandler. + * Replaces the hand-coded OpenApiServlet in consuming applications. + */ + static class Axis2OpenApiServlet extends HttpServlet { + + private static final Log log = LogFactory.getLog(Axis2OpenApiServlet.class); + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws IOException { + String uri = request.getRequestURI(); + + ConfigurationContext configContext = (ConfigurationContext) + getServletContext().getAttribute(AxisServlet.CONFIGURATION_CONTEXT); + if (configContext == null) { + log.warn("AxisServlet ConfigurationContext not found in ServletContext"); + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, + "Axis2 not initialized — AxisServlet must load before OpenApiServlet"); + return; + } + + SwaggerUIHandler handler = OpenApiModule.getSwaggerUIHandler(configContext); + if (handler == null) { + log.warn("SwaggerUIHandler not found — ensure openapi .mar is in WEB-INF/modules"); + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, + "OpenAPI module not initialized"); + return; + } + + try { + if (uri.endsWith("/openapi.json")) { + handler.handleOpenApiJsonRequest(request, response); + } else if (uri.endsWith("/openapi.yaml")) { + handler.handleOpenApiYamlRequest(request, response); + } else if (uri.endsWith("/swagger-ui") || uri.contains("/swagger-ui/")) { + handler.handleSwaggerUIRequest(request, response); + } else if (uri.endsWith("/openapi-mcp.json")) { + handler.handleMcpCatalogRequest(request, response); + } else { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } + } catch (Exception e) { + log.error("OpenApiServlet error handling " + uri + ": " + e.getMessage(), e); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } + } + } +} 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 new file mode 100644 index 0000000000..b7826c18ed --- /dev/null +++ b/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2Properties.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.axis2.spring.boot; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Configuration properties for Apache Axis2 Spring Boot integration. + * + * <p>SOAP and JSON-RPC require fundamentally different axis2.xml configurations — + * different message receivers, different dispatchers, and different content-type + * handling. The {@code axis2.mode} property selects which built-in template to use. + * Alternatively, provide your own axis2.xml via {@code axis2.configuration-file}. + * + * <p>Example {@code application.properties}: + * <pre> + * axis2.enabled=true + * axis2.mode=json + * axis2.services-path=/services + * axis2.openapi.enabled=true + * </pre> + */ +@ConfigurationProperties(prefix = "axis2") +public class Axis2Properties { + + /** + * Whether Axis2 autoconfiguration is active. + * Set to false to disable Axis2 servlet registration entirely + * (useful for server roles that don't serve web services). + */ + private boolean enabled = true; + + /** + * Protocol mode: "soap" or "json". + * + * <p>This selects the built-in axis2.xml template: + * <ul> + * <li><b>soap</b> — SOAP 1.1/1.2 message receivers (RawXMLINOutMessageReceiver), + * full SOAP dispatcher stack (SOAPAction, RequestURI, SOAPMessageBody, etc.), + * enableJSONOnly=false. This is the classic Axis2 configuration used since 1.0 + * (2006). Choose this for WSDL-first services, WS-Security, or interop with + * .NET/PHP SOAP clients.</li> + * <li><b>json</b> — JSON-RPC message receivers (JsonRpcMessageReceiver), + * JSONBasedDefaultDispatcher, enableJSONOnly=true. Choose this for new services, + * MCP/AI integration, and REST/OpenAPI consumers.</li> + * </ul> + * + * <p>SOAP and JSON modes cannot be mixed in a single deployment — the message + * receivers and dispatchers are incompatible. This matches Axis2's runtime behavior. + * + * <p>Ignored when {@code axis2.configuration-file} points to a custom axis2.xml. + */ + private String mode = "json"; + + /** + * Servlet mapping path for AxisServlet (default: /services). + * The servlet is registered at {services-path}/* . + */ + private String servicesPath = "/services"; + + /** + * Path to a custom axis2.xml. + * When set, overrides the built-in template selected by {@code axis2.mode}. + * Supports classpath: and file: prefixes. + * Default: empty (use built-in template based on mode). + */ + private String configurationFile = ""; + + /** + * OpenAPI and MCP sub-configuration. + */ + private OpenApi openapi = new OpenApi(); + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getMode() { + return mode; + } + + public void setMode(String mode) { + this.mode = mode; + } + + public String getServicesPath() { + return servicesPath; + } + + public void setServicesPath(String servicesPath) { + this.servicesPath = servicesPath; + } + + public String getConfigurationFile() { + return configurationFile; + } + + public void setConfigurationFile(String configurationFile) { + this.configurationFile = configurationFile; + } + + public OpenApi getOpenapi() { + return openapi; + } + + public void setOpenapi(OpenApi openapi) { + this.openapi = openapi; + } + + /** + * OpenAPI/MCP endpoint configuration. + * Only active when axis2-openapi is on the classpath. + */ + public static class OpenApi { + + /** + * Whether to register the OpenAPI servlet at the standard paths. + * Requires axis2-openapi on the classpath. + */ + private boolean enabled = true; + + /** + * Servlet paths for OpenAPI and MCP endpoints. + */ + private String[] paths = { + "/openapi.json", "/openapi.yaml", "/swagger-ui", "/openapi-mcp.json" + }; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String[] getPaths() { + return paths; + } + + public void setPaths(String[] paths) { + this.paths = paths; + } + } +} 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 new file mode 100644 index 0000000000..1327df81a5 --- /dev/null +++ b/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2RepositoryAutoConfiguration.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.axis2.spring.boot; + +import org.apache.axis2.transport.http.AxisServlet; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.servlet.ServletContextInitializer; +import org.springframework.context.annotation.Bean; +import org.springframework.core.io.ClassPathResource; + +import jakarta.servlet.ServletContext; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * Stages the Axis2 repository (axis2.xml, modules directory) into the WAR's + * WEB-INF directory at startup time. + * + * <p><b>axis2.xml selection</b>: SOAP and JSON-RPC require completely different + * axis2.xml configurations — different message receivers, different dispatchers, + * and a different enableJSONOnly setting. These modes cannot be mixed in a + * single deployment. The {@code axis2.mode} property selects which built-in + * template to use: + * <ul> + * <li><b>soap</b> — RawXMLINOutMessageReceiver, full SOAP dispatcher stack + * (SOAPAction, RequestURI, SOAPMessageBody, etc.)</li> + * <li><b>json</b> — JsonRpcMessageReceiver, JSONBasedDefaultDispatcher</li> + * </ul> + * + * <p>If {@code axis2.configuration-file} is set, the custom file is used instead + * and {@code axis2.mode} is ignored. + * + * <p><b>.aar file staging</b>: Phase 1 requires pre-built .aar files to be + * placed in WEB-INF/services/ by the Maven build (via maven-antrun-plugin or + * axis2-aar-maven-plugin). The starter does not generate .aar files at runtime. + * + * <p><b>.mar module staging</b>: When axis2-openapi is on the classpath, the + * consuming app's Maven build must copy the .jar as a .mar into WEB-INF/modules/. + * A future version of this starter may handle this automatically. + */ +@AutoConfiguration(before = Axis2ServletAutoConfiguration.class) +@ConditionalOnClass(AxisServlet.class) +@ConditionalOnProperty(prefix = "axis2", name = "enabled", havingValue = "true", matchIfMissing = true) +public class Axis2RepositoryAutoConfiguration { + + private static final Log log = LogFactory.getLog(Axis2RepositoryAutoConfiguration.class); + + private static final String BUILTIN_SOAP_CONFIG = "META-INF/axis2/axis2-soap.xml"; + private static final String BUILTIN_JSON_CONFIG = "META-INF/axis2/axis2-json.xml"; + + @Bean + public ServletContextInitializer axis2RepositoryInitializer(Axis2Properties properties) { + return (servletContext) -> { + stageAxis2Config(servletContext, properties); + }; + } + + private void stageAxis2Config(ServletContext servletContext, Axis2Properties properties) { + String webInfPath = 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/"); + return; + } + + Path confDir = Paths.get(webInfPath, "conf"); + Path axis2XmlTarget = confDir.resolve("axis2.xml"); + + // If axis2.xml already exists (pre-staged by Maven build), don't overwrite + if (Files.exists(axis2XmlTarget)) { + log.info("axis2.xml already present at " + axis2XmlTarget + " — using existing"); + return; + } + + // Resolve source: custom file or built-in template + String source = resolveAxis2XmlSource(properties); + log.info("Staging axis2.xml from " + source + " to " + axis2XmlTarget); + + try { + ClassPathResource resource = new ClassPathResource(source); + if (!resource.exists()) { + log.error("axis2.xml source not found: " + source + + " — Axis2 will fail to start. Check axis2.mode or axis2.configuration-file"); + return; + } + + Files.createDirectories(confDir); + try (InputStream in = resource.getInputStream(); + OutputStream out = Files.newOutputStream(axis2XmlTarget)) { + in.transferTo(out); + } + log.info("Staged axis2.xml (" + properties.getMode() + " mode)"); + + } catch (IOException e) { + log.error("Failed to stage axis2.xml: " + e.getMessage(), e); + } + } + + private String resolveAxis2XmlSource(Axis2Properties properties) { + // Custom file takes precedence + String customFile = properties.getConfigurationFile(); + if (customFile != null && !customFile.isEmpty()) { + // Strip "classpath:" prefix if present — ClassPathResource handles it + if (customFile.startsWith("classpath:")) { + return customFile.substring("classpath:".length()); + } + return customFile; + } + + // Built-in template based on mode + String mode = properties.getMode(); + if ("soap".equalsIgnoreCase(mode)) { + return BUILTIN_SOAP_CONFIG; + } + return BUILTIN_JSON_CONFIG; + } +} 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 new file mode 100644 index 0000000000..926e762391 --- /dev/null +++ b/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/Axis2ServletAutoConfiguration.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.axis2.spring.boot; + +import org.apache.axis2.deployment.WarBasedAxisConfigurator; +import org.apache.axis2.transport.http.AxisServlet; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.servlet.ServletContextInitializer; +import org.springframework.context.annotation.Bean; + +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletRegistration; +import java.util.Set; + +/** + * Registers {@link AxisServlet} with the servlet container. + * + * <p>This replaces the hand-coded {@code Axis2WebAppInitializer} that every + * Axis2 + Spring Boot project currently duplicates. The servlet is registered + * at the path configured by {@code axis2.services-path} (default: /services). + * + * <p>The critical step is setting {@code axis2.repository.path} as a servlet + * init-parameter. {@link WarBasedAxisConfigurator} uses this to locate + * {@code WEB-INF/services/*.aar} and {@code WEB-INF/modules/*.mar}. + * On WildFly, {@code ServletContext.getRealPath()} can fail due to VFS timing; + * setting it eagerly at startup time bypasses this. + */ +@AutoConfiguration(after = Axis2AutoConfiguration.class) +@ConditionalOnClass(AxisServlet.class) +@ConditionalOnProperty(prefix = "axis2", name = "enabled", havingValue = "true", matchIfMissing = true) +public class Axis2ServletAutoConfiguration { + + private static final Log log = LogFactory.getLog(Axis2ServletAutoConfiguration.class); + + @Bean + public ServletContextInitializer axis2ServletInitializer(Axis2Properties properties) { + return (ServletContext servletContext) -> { + registerAxisServlet(servletContext, properties); + }; + } + + private void registerAxisServlet(ServletContext servletContext, Axis2Properties properties) { + // Register AxisServlet + ServletRegistration.Dynamic axisServlet = servletContext.addServlet( + "AxisServlet", new AxisServlet()); + axisServlet.setLoadOnStartup(1); + + // Set repository path — the critical init-parameter for WarBasedAxisConfigurator. + // getRealPath() is called eagerly here to avoid WildFly VFS lazy-init issues. + String webInfPath = servletContext.getRealPath("/WEB-INF"); + if (webInfPath != null) { + axisServlet.setInitParameter( + WarBasedAxisConfigurator.PARAM_AXIS2_REPOSITORY_PATH, webInfPath); + log.info("axis2.repository.path = " + webInfPath); + } else { + log.warn("ServletContext.getRealPath(\"/WEB-INF\") returned null — " + + "AxisServlet will attempt to resolve the repository path itself"); + } + + // Map to configured path + String mapping = properties.getServicesPath() + "/*"; + Set<String> conflicts = axisServlet.addMapping(mapping); + if (!conflicts.isEmpty()) { + throw new IllegalStateException( + "AxisServlet could not be mapped to '" + mapping + "': " + conflicts); + } + + log.info("AxisServlet registered at " + mapping); + } +} diff --git a/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/package-info.java b/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/package-info.java new file mode 100644 index 0000000000..1322d5621b --- /dev/null +++ b/modules/spring-boot-starter/src/main/java/org/apache/axis2/spring/boot/package-info.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Spring Boot autoconfiguration for Apache Axis2. + * + * <p>This package provides autoconfiguration that registers + * {@link org.apache.axis2.transport.http.AxisServlet} and the OpenAPI/MCP + * servlet with sensible defaults for both SOAP and JSON-RPC modes. + * + * <p>For the full user guide — including how AxisServlet works, SOAP vs JSON + * mode selection, configuration properties, and migration steps — see + * {@code src/site/xdoc/docs/spring-boot-starter.xml} in the source tree. + * + * @see org.apache.axis2.spring.boot.Axis2Properties + * @see org.apache.axis2.spring.boot.Axis2AutoConfiguration + */ +package org.apache.axis2.spring.boot; diff --git a/modules/spring-boot-starter/src/main/resources/META-INF/axis2/axis2-json.xml b/modules/spring-boot-starter/src/main/resources/META-INF/axis2/axis2-json.xml new file mode 100644 index 0000000000..b0f955b614 --- /dev/null +++ b/modules/spring-boot-starter/src/main/resources/META-INF/axis2/axis2-json.xml @@ -0,0 +1,347 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, + ~ software distributed under the License is distributed on an + ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + ~ KIND, either express or implied. See the License for the + ~ specific language governing permissions and limitations + ~ under the License. + --> + +<axisconfig name="AxisJava2.0"> + <!-- ================================================= --> + <!-- Parameters --> + <!-- ================================================= --> + <parameter name="hotdeployment">true</parameter> + <parameter name="hotupdate">false</parameter> + <parameter name="enableMTOM">false</parameter> + <parameter name="enableSwA">false</parameter> + <parameter name="enableJSONOnly">true</parameter> + + <!--Uncomment if you want to enable file caching for attachments --> + <!--parameter name="cacheAttachments">true</parameter> + <parameter name="attachmentDIR"></parameter> + <parameter name="sizeThreshold">4000</parameter--> + + <parameter name="EnableChildFirstClassLoading">false</parameter> + + <!-- + The exposeServiceMetadata parameter decides whether the metadata (WSDL, schema, policy) of + the services deployed on Axis2 should be visible when ?wsdl, ?wsdl2, ?xsd, ?policy requests + are received. + This parameter can be defined in the axi2.xml file, in which case this will be applicable + globally, or in the services.xml files, in which case, it will be applicable to the + Service groups and/or services, depending on the level at which the parameter is declared. + This value of this parameter defaults to true. + --> + <parameter name="exposeServiceMetadata">true</parameter> + + + <!--Uncomment if you want to plugin your own attachments lifecycle implementation --> + <!--<attachmentsLifecycleManager class="org.apache.axiom.attachments.lifecycle.impl.LifecycleManagerImpl"/>--> + + + <!--Uncomment if you want to enable the reduction of the in-memory cache of WSDL definitions --> + <!--In some server environments, the available memory heap is limited and can fill up under load --> + <!--Since in-memory copies of WSDL definitions can be large, some steps can be taken--> + <!--to reduce the memory needed for the cached WSDL definitions. --> + <!--parameter name="reduceWSDLMemoryCache">true</parameter--> + + <!--This will give out the timout of the configuration contexts, in milliseconds--> + <parameter name="ConfigContextTimeoutInterval">30000</parameter> + + <!--During a fault, stack trace can be sent with the fault message. The following flag will control --> + <!--that behavior.--> + <parameter name="sendStacktraceDetailsWithFaults">false</parameter> + + <!--If there aren't any information available to find out the fault reason, we set the message of the exception--> + <!--as the faultreason/Reason. But when a fault is thrown from a service or some where, it will be --> + <!--wrapped by different levels. Due to this the initial exception message can be lost. If this flag--> + <!--is set, then Axis2 tries to get the first exception and set its message as the faultreason/Reason.--> + <parameter name="DrillDownToRootCauseForFaultReason">false</parameter> + + <parameter name="userName"></parameter> + <parameter name="password"></parameter> + + <!--To override repository/services you need to uncomment following parameter and value SHOULD be absolute file path.--> + <!--ServicesDirectory only works on the following cases--> + <!---File based configurator and in that case the value should be a file URL (http:// not allowed)--> + <!---When creating URL Based configurator with URL file:// --> + <!--- War based configurator with expanded case , --> + + <!--All the other scenarios it will be ignored.--> + <!--<parameter name="ServicesDirectory">service</parameter>--> + <!--To override repository/modules you need to uncomment following parameter and value SHOULD be absolute file path--> + <!--<parameter name="ModulesDirectory">modules</parameter>--> + + + <!--Following params will set the proper context paths for invocations. All the endpoints will have a commons context--> + <!--root which can configured using the following contextRoot parameter--> + <!--<parameter name="contextRoot">axis2</parameter>--> + + <!--Our HTTP endpoints can handle both REST and SOAP. Following parameters can be used to distinguiush those endpoints--> + <!--In case of a servlet, if you change this you have to manually change the settings of your servlet container to map this --> + <!--context path to proper Axis2 servlets--> + <!--<parameter name="servicePath">services</parameter>--> + <!--<parameter name="restPath">rest</parameter>--> + + <!-- Following parameter will completely disable REST handling in Axis2--> + <parameter name="disableREST" locked="false">false</parameter> + + <!-- Following parameter will suppress generation of SOAP 1.2 bindings in auto-generated WSDL files --> + <parameter name="disableSOAP12" locked="true">false</parameter> + + <!-- ================================================= --> + <!-- Deployers --> + <!-- ================================================= --> + + <!--Service deployer , this will alow users to deploy AAR or exploded AAR as axis2 services--> + <deployer extension=".aar" directory="services" class="org.apache.axis2.deployment.ServiceDeployer"> + <serviceBuilderExtension name ="jwsbuilderExt" class="org.apache.axis2.jaxws.framework.JAXWSServiceBuilderExtension"/> + <serviceBuilderExtension name ="wsdlbuilderExt" class="org.apache.axis2.deployment.WSDLServiceBuilderExtension"/> + </deployer> + + <!--POJO deployer , this will alow users to drop .class file and make that into a service--> + <deployer extension=".class" directory="pojo" class="org.apache.axis2.deployment.POJODeployer"/> + <deployer extension=".jar" directory="transports" + class="org.apache.axis2.deployment.TransportDeployer"/> + + <!--CORBA deployer , this will alow users to invoke remote CORBA services through Axis2--> + <!--<deployer extension=".xml" directory="corba" class="org.apache.axis2.corba.deployer.CorbaDeployer"/>--> + + <!--<deployer extension=".jsa" directory="rmiservices" class="org.apache.axis2.rmi.deploy.RMIServiceDeployer"/>--> + + + <!-- Following parameter will set the host name for the epr--> + <!--<parameter name="hostname" locked="true">myhost.com</parameter>--> + + <!-- If you have a front end host which exposes this webservice using a different public URL --> + <!-- use this parameter to override autodetected url --> + <!--<parameter name="httpFrontendHostUrl">https://someotherhost/context</parameter>--> + + <!--By default, JAXWS services are created by reading annotations. WSDL and schema are generated--> + <!--using a separate WSDL generator only when ?wsdl is called. Therefore, even if you engage--> + <!--policies etc.. to AxisService, it doesn't appear in the WSDL. By setting the following property--> + <!--to true, you can create the AxisService using the generated WSDL and remove the need for a--> + <!--WSDL generator. When ?wsdl is called, WSDL is generated in the normal way.--> + <parameter name="useGeneratedWSDLinJAXWS">false</parameter> + + <!-- The way of adding listener to the system--> + <!-- <listener class="org.apache.axis2.ObserverIMPL">--> + <!-- <parameter name="RSS_URL">http://127.0.0.1/rss</parameter>--> + <!-- </listener>--> + + <threadContextMigrators> + <threadContextMigrator listId="JAXWS-ThreadContextMigrator-List" + class="org.apache.axis2.jaxws.addressing.migrator.EndpointContextMapMigrator"/> + </threadContextMigrators> + + <!-- ================================================= --> + <!-- Message Receivers --> + <!-- ================================================= --> + <!--This is the default MessageReceiver for the system , if you want to have MessageReceivers for --> + <!--all the other MEP implement it and add the correct entry to here , so that you can refer from--> + <!--any operation --> + <!--Note : You can override this for a particular service by adding the same element with your requirement--> + <messageReceivers> + <messageReceiver mep="http://www.w3.org/ns/wsdl/in-out" + class="org.apache.axis2.json.moshi.rpc.JsonRpcMessageReceiver" /> + <messageReceiver mep="http://www.w3.org/ns/wsdl/in-only" + class="org.apache.axis2.json.moshi.rpc.JsonInOnlyRPCMessageReceiver"/> + </messageReceivers> + + <!-- ================================================= --> + <!-- Message Formatter --> + <!-- ================================================= --> + <!--Following content type to message formatter mapping can be used to implement support for different message --> + <!--format serialization in Axis2. These message formats are expected to be resolved based on the content type. --> + <messageFormatters> + <messageFormatter contentType="application/x-www-form-urlencoded" + class="org.apache.axis2.kernel.http.XFormURLEncodedFormatter"/> + <messageFormatter contentType="multipart/form-data" + class="org.apache.axis2.kernel.http.MultipartFormDataFormatter"/> + <messageFormatter contentType="application/xml" + class="org.apache.axis2.kernel.http.ApplicationXMLFormatter"/> + <messageFormatter contentType="text/xml" + class="org.apache.axis2.kernel.http.SOAPMessageFormatter"/> + <messageFormatter contentType="application/soap+xml" + class="org.apache.axis2.kernel.http.SOAPMessageFormatter"/> + <messageFormatter contentType="application/json" + class="org.apache.axis2.json.moshi.JsonFormatter"/> + </messageFormatters> + + <!-- ================================================= --> + <!-- Message Builders --> + <!-- ================================================= --> + <!--Following content type to builder mapping can be used to implement support for different message --> + <!--formats in Axis2. These message formats are expected to be resolved based on the content type. --> + <messageBuilders> + <messageBuilder contentType="application/xml" + class="org.apache.axis2.builder.ApplicationXMLBuilder"/> + <messageBuilder contentType="application/x-www-form-urlencoded" + class="org.apache.axis2.builder.XFormURLEncodedBuilder"/> + <messageBuilder contentType="multipart/form-data" + class="org.apache.axis2.builder.MultipartFormDataBuilder"/> + <messageBuilder contentType="application/json" + class="org.apache.axis2.json.moshi.JsonBuilder"/> + </messageBuilders> + + <!-- ================================================= --> + <!-- Transport Ins --> + <!-- ================================================= --> + <transportReceiver name="http" class="org.apache.axis2.transport.http.AxisServletListener"> + <parameter name="port">8080</parameter> + </transportReceiver> + + <transportReceiver name="https" class="org.apache.axis2.transport.http.AxisServletListener"> + <parameter name="port">8443</parameter> + </transportReceiver> + + <!-- This is where you'd put custom transports. See the transports project --> + <!-- for more. http://ws.apache.org/commons/transport --> + + <!-- ================================================= --> + <!-- Transport Outs --> + <!-- ================================================= --> + + <transportSender name="local" + class="org.apache.axis2.transport.local.LocalTransportSender"/> + <transportSender name="http" + class="org.apache.axis2.transport.http.impl.httpclient5.HTTPClient5TransportSender"> + <parameter name="PROTOCOL">HTTP/1.1</parameter> + <parameter name="Transfer-Encoding">chunked</parameter> + + <!-- If following is set to 'true', optional action part of the Content-Type will not be added to the SOAP 1.2 messages --> + <!-- <parameter name="OmitSOAP12Action">true</parameter> --> + </transportSender> + + <transportSender name="https" + class="org.apache.axis2.transport.http.impl.httpclient5.HTTPClient5TransportSender"> + <parameter name="PROTOCOL">HTTP/1.1</parameter> + <parameter name="Transfer-Encoding">chunked</parameter> + </transportSender> + + <!-- HTTP/2 Transport for Enterprise Big Data Processing --> + <transportSender name="h2" + class="org.apache.axis2.transport.h2.impl.httpclient5.H2TransportSender"> + <parameter name="PROTOCOL">HTTP/2.0</parameter> + <parameter name="maxConcurrentStreams">100</parameter> + <parameter name="initialWindowSize">65536</parameter> + <parameter name="serverPushEnabled">false</parameter> + <parameter name="connectionTimeout">30000</parameter> + <parameter name="responseTimeout">300000</parameter> + <parameter name="streamingBufferSize">65536</parameter> + <parameter name="memoryPressureThreshold">0.8</parameter> + <!-- Enterprise Big Data Configuration --> + <parameter name="enableStreamingOptimization">true</parameter> + <parameter name="enableMemoryOptimization">true</parameter> + <parameter name="largePayloadThreshold">52428800</parameter> <!-- 50MB --> + </transportSender> + + <!-- Please enable this if you need the java transport --> + <!-- <transportSender name="java" + class="org.apache.axis2.transport.java.JavaTransportSender"/> --> + + <!-- ================================================= --> + <!-- Global Modules --> + <!-- ================================================= --> + <!-- Comment this to disable Addressing --> + <!-- commented out for the Axis2 spring boot demo that uses JSON --> + <!-- <module ref="addressing"/> --> + + <!-- OpenAPI/Swagger documentation module --> + <module ref="openapi"/> + + <!--Configuring module , providing parameters for modules whether they refer or not--> + <!--<moduleConfig name="addressing">--> + <!--<parameter name="addressingPara">N/A</parameter>--> + <!--</moduleConfig>--> + + + <!-- ================================================= --> + <!-- Phases --> + <!-- ================================================= --> + <phaseOrder type="InFlow"> + <!-- System predefined phases --> + <phase name="Transport"> + <handler name="RequestURIBasedDispatcher" + class="org.apache.axis2.dispatchers.RequestURIBasedDispatcher"> + <order phase="Transport"/> + </handler> + <handler name="SOAPActionBasedDispatcher" + class="org.apache.axis2.dispatchers.SOAPActionBasedDispatcher"> + <order phase="Transport"/> + </handler> + <handler name="JSONMessageHandler" + class="org.apache.axis2.json.moshi.JSONMessageHandler"> + <order phase="Transport"/> + </handler> + </phase> + <phase name="Addressing"> + <handler name="AddressingBasedDispatcher" + class="org.apache.axis2.dispatchers.AddressingBasedDispatcher"> + <order phase="Addressing"/> + </handler> + </phase> + <phase name="Security"/> + <phase name="PreDispatch"/> + <phase name="Dispatch" class="org.apache.axis2.engine.DispatchPhase"> + <handler name="JSONBasedDefaultDispatcher" + class="org.apache.axis2.dispatchers.JSONBasedDefaultDispatcher"/> + </phase> + <phase name="RMPhase"/> + <!-- System predefined phases --> + <!-- After Postdispatch phase module author or service author can add any phase he want --> + <phase name="OperationInPhase"> + <handler name="MustUnderstandChecker" + class="org.apache.axis2.jaxws.dispatchers.MustUnderstandChecker"> + <order phase="OperationInPhase"/> + </handler> + </phase> + </phaseOrder> + <phaseOrder type="OutFlow"> + <!-- user can add his own phases to this area --> + <phase name="OperationOutPhase"/> + <!--system predefined phase--> + <!--these phase will run irrespective of the service--> + <phase name="RMPhase"/> + <phase name="PolicyDetermination"/> + <phase name="MessageOut"/> + <phase name="Security"/> + </phaseOrder> + <phaseOrder type="InFaultFlow"> + <phase name="Addressing"> + <handler name="AddressingBasedDispatcher" + class="org.apache.axis2.dispatchers.AddressingBasedDispatcher"> + <order phase="Addressing"/> + </handler> + </phase> + <phase name="Security"/> + <phase name="PreDispatch"/> + <phase name="Dispatch" class="org.apache.axis2.engine.DispatchPhase"> + <handler name="JSONBasedDefaultDispatcher" + class="org.apache.axis2.dispatchers.JSONBasedDefaultDispatcher"/> + </phase> + <phase name="RMPhase"/> + <!-- user can add his own phases to this area --> + <phase name="OperationInFaultPhase"/> + </phaseOrder> + <phaseOrder type="OutFaultFlow"> + <!-- user can add his own phases to this area --> + <phase name="OperationOutFaultPhase"/> + <phase name="RMPhase"/> + <phase name="PolicyDetermination"/> + <phase name="MessageOut"/> + <phase name="Security"/> + </phaseOrder> +</axisconfig> + diff --git a/modules/spring-boot-starter/src/main/resources/META-INF/axis2/axis2-soap.xml b/modules/spring-boot-starter/src/main/resources/META-INF/axis2/axis2-soap.xml new file mode 100644 index 0000000000..fab7a700eb --- /dev/null +++ b/modules/spring-boot-starter/src/main/resources/META-INF/axis2/axis2-soap.xml @@ -0,0 +1,363 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, + ~ software distributed under the License is distributed on an + ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + ~ KIND, either express or implied. See the License for the + ~ specific language governing permissions and limitations + ~ under the License. + --> + +<axisconfig name="AxisJava2.0"> + <!-- ================================================= --> + <!-- Parameters --> + <!-- ================================================= --> + <parameter name="hotdeployment">true</parameter> + <parameter name="hotupdate">false</parameter> + <parameter name="enableMTOM">false</parameter> + <parameter name="enableSwA">false</parameter> + <parameter name="enableJSONOnly">false</parameter> + + <!--Uncomment if you want to enable file caching for attachments --> + <!--parameter name="cacheAttachments">true</parameter> + <parameter name="attachmentDIR"></parameter> + <parameter name="sizeThreshold">4000</parameter--> + + <parameter name="EnableChildFirstClassLoading">false</parameter> + + <!-- + The exposeServiceMetadata parameter decides whether the metadata (WSDL, schema, policy) of + the services deployed on Axis2 should be visible when ?wsdl, ?wsdl2, ?xsd, ?policy requests + are received. + This parameter can be defined in the axi2.xml file, in which case this will be applicable + globally, or in the services.xml files, in which case, it will be applicable to the + Service groups and/or services, depending on the level at which the parameter is declared. + This value of this parameter defaults to true. + --> + <parameter name="exposeServiceMetadata">true</parameter> + + + <!--Uncomment if you want to plugin your own attachments lifecycle implementation --> + <!--<attachmentsLifecycleManager class="org.apache.axiom.attachments.lifecycle.impl.LifecycleManagerImpl"/>--> + + + <!--Uncomment if you want to enable the reduction of the in-memory cache of WSDL definitions --> + <!--In some server environments, the available memory heap is limited and can fill up under load --> + <!--Since in-memory copies of WSDL definitions can be large, some steps can be taken--> + <!--to reduce the memory needed for the cached WSDL definitions. --> + <!--parameter name="reduceWSDLMemoryCache">true</parameter--> + + <!--This will give out the timout of the configuration contexts, in milliseconds--> + <parameter name="ConfigContextTimeoutInterval">30000</parameter> + + <!--During a fault, stack trace can be sent with the fault message. The following flag will control --> + <!--that behavior.--> + <parameter name="sendStacktraceDetailsWithFaults">false</parameter> + + <!--If there aren't any information available to find out the fault reason, we set the message of the exception--> + <!--as the faultreason/Reason. But when a fault is thrown from a service or some where, it will be --> + <!--wrapped by different levels. Due to this the initial exception message can be lost. If this flag--> + <!--is set, then Axis2 tries to get the first exception and set its message as the faultreason/Reason.--> + <parameter name="DrillDownToRootCauseForFaultReason">false</parameter> + + <parameter name="userName"></parameter> + <parameter name="password"></parameter> + + <!--To override repository/services you need to uncomment following parameter and value SHOULD be absolute file path.--> + <!--ServicesDirectory only works on the following cases--> + <!---File based configurator and in that case the value should be a file URL (http:// not allowed)--> + <!---When creating URL Based configurator with URL file:// --> + <!--- War based configurator with expanded case , --> + + <!--All the other scenarios it will be ignored.--> + <!--<parameter name="ServicesDirectory">service</parameter>--> + <!--To override repository/modules you need to uncomment following parameter and value SHOULD be absolute file path--> + <!--<parameter name="ModulesDirectory">modules</parameter>--> + + + <!--Following params will set the proper context paths for invocations. All the endpoints will have a commons context--> + <!--root which can configured using the following contextRoot parameter--> + <!--<parameter name="contextRoot">axis2</parameter>--> + + <!--Our HTTP endpoints can handle both REST and SOAP. Following parameters can be used to distinguiush those endpoints--> + <!--In case of a servlet, if you change this you have to manually change the settings of your servlet container to map this --> + <!--context path to proper Axis2 servlets--> + <!--<parameter name="servicePath">services</parameter>--> + <!--<parameter name="restPath">rest</parameter>--> + + <!-- Following parameter will completely disable REST handling in Axis2--> + <parameter name="disableREST" locked="false">false</parameter> + + <!-- Following parameter will suppress generation of SOAP 1.2 bindings in auto-generated WSDL files --> + <parameter name="disableSOAP12" locked="true">false</parameter> + + <!-- ================================================= --> + <!-- Deployers --> + <!-- ================================================= --> + + <!--Service deployer , this will alow users to deploy AAR or exploded AAR as axis2 services--> + <deployer extension=".aar" directory="services" class="org.apache.axis2.deployment.ServiceDeployer"> + <serviceBuilderExtension name ="jwsbuilderExt" class="org.apache.axis2.jaxws.framework.JAXWSServiceBuilderExtension"/> + <serviceBuilderExtension name ="wsdlbuilderExt" class="org.apache.axis2.deployment.WSDLServiceBuilderExtension"/> + </deployer> + + <!--POJO deployer , this will alow users to drop .class file and make that into a service--> + <deployer extension=".class" directory="pojo" class="org.apache.axis2.deployment.POJODeployer"/> + <deployer extension=".jar" directory="transports" + class="org.apache.axis2.deployment.TransportDeployer"/> + + <!--CORBA deployer , this will alow users to invoke remote CORBA services through Axis2--> + <!--<deployer extension=".xml" directory="corba" class="org.apache.axis2.corba.deployer.CorbaDeployer"/>--> + + <!--<deployer extension=".jsa" directory="rmiservices" class="org.apache.axis2.rmi.deploy.RMIServiceDeployer"/>--> + + + <!-- Following parameter will set the host name for the epr--> + <!--<parameter name="hostname" locked="true">myhost.com</parameter>--> + + <!-- If you have a front end host which exposes this webservice using a different public URL --> + <!-- use this parameter to override autodetected url --> + <!--<parameter name="httpFrontendHostUrl">https://someotherhost/context</parameter>--> + + <!--By default, JAXWS services are created by reading annotations. WSDL and schema are generated--> + <!--using a separate WSDL generator only when ?wsdl is called. Therefore, even if you engage--> + <!--policies etc.. to AxisService, it doesn't appear in the WSDL. By setting the following property--> + <!--to true, you can create the AxisService using the generated WSDL and remove the need for a--> + <!--WSDL generator. When ?wsdl is called, WSDL is generated in the normal way.--> + <parameter name="useGeneratedWSDLinJAXWS">false</parameter> + + <!-- The way of adding listener to the system--> + <!-- <listener class="org.apache.axis2.ObserverIMPL">--> + <!-- <parameter name="RSS_URL">http://127.0.0.1/rss</parameter>--> + <!-- </listener>--> + + <threadContextMigrators> + <threadContextMigrator listId="JAXWS-ThreadContextMigrator-List" + class="org.apache.axis2.jaxws.addressing.migrator.EndpointContextMapMigrator"/> + </threadContextMigrators> + + <!-- ================================================= --> + <!-- Message Receivers --> + <!-- ================================================= --> + <!--This is the default MessageReceiver for the system , if you want to have MessageReceivers for --> + <!--all the other MEP implement it and add the correct entry to here , so that you can refer from--> + <!--any operation --> + <!--Note : You can override this for a particular service by adding the same element with your requirement--> + <messageReceivers> + <messageReceiver mep="http://www.w3.org/ns/wsdl/in-only" + class="org.apache.axis2.receivers.RawXMLINOnlyMessageReceiver"/> + <messageReceiver mep="http://www.w3.org/ns/wsdl/in-out" + class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/> + </messageReceivers> + + <!-- ================================================= --> + <!-- Message Formatter --> + <!-- ================================================= --> + <!--Following content type to message formatter mapping can be used to implement support for different message --> + <!--format serialization in Axis2. These message formats are expected to be resolved based on the content type. --> + <messageFormatters> + <messageFormatter contentType="application/x-www-form-urlencoded" + class="org.apache.axis2.kernel.http.XFormURLEncodedFormatter"/> + <messageFormatter contentType="multipart/form-data" + class="org.apache.axis2.kernel.http.MultipartFormDataFormatter"/> + <messageFormatter contentType="application/xml" + class="org.apache.axis2.kernel.http.ApplicationXMLFormatter"/> + <messageFormatter contentType="text/xml" + class="org.apache.axis2.kernel.http.SOAPMessageFormatter"/> + <messageFormatter contentType="application/soap+xml" + class="org.apache.axis2.kernel.http.SOAPMessageFormatter"/> + <messageFormatter contentType="application/json" + class="org.apache.axis2.json.moshi.JsonFormatter"/> + </messageFormatters> + + <!-- ================================================= --> + <!-- Message Builders --> + <!-- ================================================= --> + <!--Following content type to builder mapping can be used to implement support for different message --> + <!--formats in Axis2. These message formats are expected to be resolved based on the content type. --> + <messageBuilders> + <messageBuilder contentType="application/xml" + class="org.apache.axis2.builder.ApplicationXMLBuilder"/> + <messageBuilder contentType="application/x-www-form-urlencoded" + class="org.apache.axis2.builder.XFormURLEncodedBuilder"/> + <messageBuilder contentType="multipart/form-data" + class="org.apache.axis2.builder.MultipartFormDataBuilder"/> + <messageBuilder contentType="application/json" + class="org.apache.axis2.json.moshi.JsonBuilder"/> + </messageBuilders> + + <!-- ================================================= --> + <!-- Transport Ins --> + <!-- ================================================= --> + <transportReceiver name="http" class="org.apache.axis2.transport.http.AxisServletListener"> + <parameter name="port">8080</parameter> + </transportReceiver> + + <transportReceiver name="https" class="org.apache.axis2.transport.http.AxisServletListener"> + <parameter name="port">8443</parameter> + </transportReceiver> + + <!-- This is where you'd put custom transports. See the transports project --> + <!-- for more. http://ws.apache.org/commons/transport --> + + <!-- ================================================= --> + <!-- Transport Outs --> + <!-- ================================================= --> + + <transportSender name="local" + class="org.apache.axis2.transport.local.LocalTransportSender"/> + <transportSender name="http" + class="org.apache.axis2.transport.http.impl.httpclient5.HTTPClient5TransportSender"> + <parameter name="PROTOCOL">HTTP/1.1</parameter> + <parameter name="Transfer-Encoding">chunked</parameter> + + <!-- If following is set to 'true', optional action part of the Content-Type will not be added to the SOAP 1.2 messages --> + <!-- <parameter name="OmitSOAP12Action">true</parameter> --> + </transportSender> + + <transportSender name="https" + class="org.apache.axis2.transport.http.impl.httpclient5.HTTPClient5TransportSender"> + <parameter name="PROTOCOL">HTTP/1.1</parameter> + <parameter name="Transfer-Encoding">chunked</parameter> + </transportSender> + + <!-- HTTP/2 Transport for Enterprise Big Data Processing --> + <transportSender name="h2" + class="org.apache.axis2.transport.h2.impl.httpclient5.H2TransportSender"> + <parameter name="PROTOCOL">HTTP/2.0</parameter> + <parameter name="maxConcurrentStreams">100</parameter> + <parameter name="initialWindowSize">65536</parameter> + <parameter name="serverPushEnabled">false</parameter> + <parameter name="connectionTimeout">30000</parameter> + <parameter name="responseTimeout">300000</parameter> + <parameter name="streamingBufferSize">65536</parameter> + <parameter name="memoryPressureThreshold">0.8</parameter> + <!-- Enterprise Big Data Configuration --> + <parameter name="enableStreamingOptimization">true</parameter> + <parameter name="enableMemoryOptimization">true</parameter> + <parameter name="largePayloadThreshold">52428800</parameter> <!-- 50MB --> + </transportSender> + + <!-- Please enable this if you need the java transport --> + <!-- <transportSender name="java" + class="org.apache.axis2.transport.java.JavaTransportSender"/> --> + + <!-- ================================================= --> + <!-- Global Modules --> + <!-- ================================================= --> + <!-- Comment this to disable Addressing --> + <!-- commented out for the Axis2 spring boot demo that uses JSON --> + <!-- <module ref="addressing"/> --> + + <!-- OpenAPI/Swagger documentation module --> + <module ref="openapi"/> + + <!--Configuring module , providing parameters for modules whether they refer or not--> + <!--<moduleConfig name="addressing">--> + <!--<parameter name="addressingPara">N/A</parameter>--> + <!--</moduleConfig>--> + + + <!-- ================================================= --> + <!-- Phases --> + <!-- ================================================= --> + <phaseOrder type="InFlow"> + <!-- System predefined phases --> + <phase name="Transport"> + <handler name="RequestURIBasedDispatcher" + class="org.apache.axis2.dispatchers.RequestURIBasedDispatcher"> + <order phase="Transport"/> + </handler> + <handler name="SOAPActionBasedDispatcher" + class="org.apache.axis2.dispatchers.SOAPActionBasedDispatcher"> + <order phase="Transport"/> + </handler> + <handler name="JSONMessageHandler" + class="org.apache.axis2.json.moshi.JSONMessageHandler"> + <order phase="Transport"/> + </handler> + </phase> + <phase name="Addressing"> + <handler name="AddressingBasedDispatcher" + class="org.apache.axis2.dispatchers.AddressingBasedDispatcher"> + <order phase="Addressing"/> + </handler> + </phase> + <phase name="Security"/> + <phase name="PreDispatch"/> + <phase name="Dispatch" class="org.apache.axis2.engine.DispatchPhase"> + <handler name="RequestURIBasedDispatcher" + class="org.apache.axis2.dispatchers.RequestURIBasedDispatcher"/> + <handler name="SOAPActionBasedDispatcher" + class="org.apache.axis2.dispatchers.SOAPActionBasedDispatcher"/> + <handler name="RequestURIOperationDispatcher" + class="org.apache.axis2.dispatchers.RequestURIOperationDispatcher"/> + <handler name="SOAPMessageBodyBasedDispatcher" + class="org.apache.axis2.dispatchers.SOAPMessageBodyBasedDispatcher"/> + <handler name="HTTPLocationBasedDispatcher" + class="org.apache.axis2.dispatchers.HTTPLocationBasedDispatcher"/> + </phase> + <phase name="RMPhase"/> + <!-- System predefined phases --> + <!-- After Postdispatch phase module author or service author can add any phase he want --> + <phase name="OperationInPhase"> + <handler name="MustUnderstandChecker" + class="org.apache.axis2.jaxws.dispatchers.MustUnderstandChecker"> + <order phase="OperationInPhase"/> + </handler> + </phase> + </phaseOrder> + <phaseOrder type="OutFlow"> + <!-- user can add his own phases to this area --> + <phase name="OperationOutPhase"/> + <!--system predefined phase--> + <!--these phase will run irrespective of the service--> + <phase name="RMPhase"/> + <phase name="PolicyDetermination"/> + <phase name="MessageOut"/> + <phase name="Security"/> + </phaseOrder> + <phaseOrder type="InFaultFlow"> + <phase name="Addressing"> + <handler name="AddressingBasedDispatcher" + class="org.apache.axis2.dispatchers.AddressingBasedDispatcher"> + <order phase="Addressing"/> + </handler> + </phase> + <phase name="Security"/> + <phase name="PreDispatch"/> + <phase name="Dispatch" class="org.apache.axis2.engine.DispatchPhase"> + <handler name="RequestURIBasedDispatcher" + class="org.apache.axis2.dispatchers.RequestURIBasedDispatcher"/> + <handler name="SOAPActionBasedDispatcher" + class="org.apache.axis2.dispatchers.SOAPActionBasedDispatcher"/> + <handler name="RequestURIOperationDispatcher" + class="org.apache.axis2.dispatchers.RequestURIOperationDispatcher"/> + <handler name="SOAPMessageBodyBasedDispatcher" + class="org.apache.axis2.dispatchers.SOAPMessageBodyBasedDispatcher"/> + <handler name="HTTPLocationBasedDispatcher" + class="org.apache.axis2.dispatchers.HTTPLocationBasedDispatcher"/> + </phase> + <phase name="RMPhase"/> + <!-- user can add his own phases to this area --> + <phase name="OperationInFaultPhase"/> + </phaseOrder> + <phaseOrder type="OutFaultFlow"> + <!-- user can add his own phases to this area --> + <phase name="OperationOutFaultPhase"/> + <phase name="RMPhase"/> + <phase name="PolicyDetermination"/> + <phase name="MessageOut"/> + <phase name="Security"/> + </phaseOrder> +</axisconfig> + diff --git a/modules/spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/modules/spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000000..b5781fab91 --- /dev/null +++ b/modules/spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,4 @@ +org.apache.axis2.spring.boot.Axis2AutoConfiguration +org.apache.axis2.spring.boot.Axis2RepositoryAutoConfiguration +org.apache.axis2.spring.boot.Axis2ServletAutoConfiguration +org.apache.axis2.spring.boot.Axis2OpenApiAutoConfiguration 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 new file mode 100644 index 0000000000..e7e0cee3e0 --- /dev/null +++ b/modules/spring-boot-starter/src/test/java/org/apache/axis2/spring/boot/Axis2AutoConfigurationTest.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.axis2.spring.boot; + +import org.junit.jupiter.api.Test; +import org.springframework.core.io.ClassPathResource; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for axis2-spring-boot-starter autoconfiguration. + */ +class Axis2AutoConfigurationTest { + + @Test + void jsonTemplateExistsOnClasspath() { + ClassPathResource resource = new ClassPathResource("META-INF/axis2/axis2-json.xml"); + assertTrue(resource.exists(), "axis2-json.xml template must be on classpath"); + } + + @Test + void soapTemplateExistsOnClasspath() { + ClassPathResource resource = new ClassPathResource("META-INF/axis2/axis2-soap.xml"); + assertTrue(resource.exists(), "axis2-soap.xml template must be on classpath"); + } + + @Test + void jsonTemplateHasEnableJsonOnlyTrue() throws Exception { + ClassPathResource resource = new ClassPathResource("META-INF/axis2/axis2-json.xml"); + String content = new String(resource.getInputStream().readAllBytes()); + assertTrue(content.contains("\"enableJSONOnly\">true</parameter>"), + "JSON template must have enableJSONOnly=true"); + assertTrue(content.contains("JSONBasedDefaultDispatcher"), + "JSON template must use JSONBasedDefaultDispatcher"); + assertFalse(content.contains("RawXMLINOutMessageReceiver"), + "JSON template must not have SOAP message receivers"); + } + + @Test + void soapTemplateHasEnableJsonOnlyFalse() throws Exception { + ClassPathResource resource = new ClassPathResource("META-INF/axis2/axis2-soap.xml"); + String content = new String(resource.getInputStream().readAllBytes()); + assertTrue(content.contains("\"enableJSONOnly\">false</parameter>"), + "SOAP template must have enableJSONOnly=false"); + assertTrue(content.contains("RawXMLINOutMessageReceiver"), + "SOAP template must have SOAP message receivers"); + assertTrue(content.contains("SOAPActionBasedDispatcher"), + "SOAP template must have SOAP dispatcher stack"); + assertFalse(content.contains("JSONBasedDefaultDispatcher"), + "SOAP template must not have JSON dispatcher"); + } + + @Test + void defaultPropertiesAreCorrect() { + Axis2Properties props = new Axis2Properties(); + assertTrue(props.isEnabled(), "axis2.enabled defaults to true"); + 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"); + assertTrue(props.getOpenapi().isEnabled(), "axis2.openapi.enabled defaults to true"); + } + + @Test + void soapModeSelectsSoapTemplate() { + Axis2Properties props = new Axis2Properties(); + props.setMode("soap"); + Axis2RepositoryAutoConfiguration config = new Axis2RepositoryAutoConfiguration(); + // The resolveAxis2XmlSource is private, but we verify via the template content test above + assertEquals("soap", props.getMode()); + } + + @Test + void customConfigFileOverridesMode() { + Axis2Properties props = new Axis2Properties(); + props.setMode("json"); + props.setConfigurationFile("classpath:my-custom-axis2.xml"); + assertFalse(props.getConfigurationFile().isEmpty(), + "Custom config file should override mode-based selection"); + } + + @Test + void autoconfigurationImportsFileExists() { + ClassPathResource resource = new ClassPathResource( + "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports"); + assertTrue(resource.exists(), "AutoConfiguration.imports must be on classpath"); + } + + @Test + void autoconfigurationImportsContainsAllClasses() throws Exception { + ClassPathResource resource = new ClassPathResource( + "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports"); + String content = new String(resource.getInputStream().readAllBytes()); + assertTrue(content.contains("Axis2AutoConfiguration"), + "Must list Axis2AutoConfiguration"); + assertTrue(content.contains("Axis2RepositoryAutoConfiguration"), + "Must list Axis2RepositoryAutoConfiguration"); + assertTrue(content.contains("Axis2ServletAutoConfiguration"), + "Must list Axis2ServletAutoConfiguration"); + assertTrue(content.contains("Axis2OpenApiAutoConfiguration"), + "Must list Axis2OpenApiAutoConfiguration"); + } +} diff --git a/pom.xml b/pom.xml index 7c1afbd162..4e9e004c81 100644 --- a/pom.xml +++ b/pom.xml @@ -398,6 +398,7 @@ <module>modules/soapmonitor/module</module> <module>modules/schema-validation</module> <module>modules/spring</module> + <module>modules/spring-boot-starter</module> <module>modules/testutils</module> <module>modules/tool/maven-shared</module> <module>modules/tool/axis2-aar-maven-plugin</module> diff --git a/src/site/xdoc/docs/spring-boot-starter.xml b/src/site/xdoc/docs/spring-boot-starter.xml new file mode 100644 index 0000000000..51b3705c39 --- /dev/null +++ b/src/site/xdoc/docs/spring-boot-starter.xml @@ -0,0 +1,408 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, + ~ software distributed under the License is distributed on an + ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + ~ KIND, either express or implied. See the License for the + ~ specific language governing permissions and limitations + ~ under the License. + --> + +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" + "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> +<html> +<head> + <meta http-equiv="content-type" content=""/> + <title>Apache Axis2 Spring Boot Starter</title> +</head> + +<body dir="ltr" lang="en-US"> + +<h1 align="center">Apache Axis2 Spring Boot Starter</h1> + +<p><strong>axis2-spring-boot-starter</strong> reduces Axis2 + Spring Boot integration +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> + +<ul> +<li><a href="#deployment_model">0. Deployment Model (WAR Only)</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> +<li><a href="#soap_vs_json">4. SOAP Mode vs JSON Mode</a></li> +<li><a href="#what_autoconfigured">5. What Gets Autoconfigured</a></li> +<li><a href="#what_stays">6. What Stays in Your Application</a></li> +<li><a href="#openapi_mcp">7. OpenAPI and MCP Support</a></li> +<li><a href="#migration">8. Migration from Manual Configuration</a></li> +<li><a href="#containers">9. Container Support</a></li> +</ul> + +<!-- ============================================================ --> +<a name="deployment_model"/> +<h2>0. Deployment Model (WAR Only)</h2> + +<p>This starter supports <strong>WAR deployment to an external servlet +container</strong> — the same deployment model used by most organizations running +Axis2 in production. Your Spring Boot application must extend +<code>SpringBootServletInitializer</code> and be packaged as a WAR.</p> + +<p><strong>Spring Boot embedded Tomcat (<code>java -jar</code>) is not supported.</strong></p> + +<h3>Why not embedded?</h3> + +<p>Axis2's <code>WarBasedAxisConfigurator</code> requires a real filesystem layout: +<code>WEB-INF/conf/axis2.xml</code>, <code>WEB-INF/services/*.aar</code>, and +<code>WEB-INF/modules/*.mar</code>. An embedded Spring Boot application runs from +a fat JAR with no exploded WAR directory. Supporting this would require runtime +classpath scanning for <code>.aar</code> files, temp directory staging, and a +new configurator implementation — work that is planned but not yet complete.</p> + +<p>Additionally, external containers provide operational advantages for the +long-running, stateful service deployments typical of Axis2 workloads:</p> + +<ul> +<li>Container-managed thread pools, connection pools, and JMX monitoring</li> +<li>Hot-redeploy via exploded WAR without JVM restart</li> +<li>Central TLS/certificate management (Tomcat's <code>server.xml</code>, WildFly's Elytron)</li> +<li>Established operational tooling (WildFly CLI, Tomcat Manager, deployment scanners)</li> +</ul> + +<h3>Which external containers?</h3> + +<table border="1"> +<tr><th>Container</th><th>JDK</th><th>Tested</th></tr> +<tr><td>Apache Tomcat 11</td><td>OpenJDK 21, OpenJDK 25</td><td>Yes</td></tr> +<tr><td>WildFly 32</td><td>OpenJDK 21</td><td>Yes</td></tr> +<tr><td>WildFly 39</td><td>OpenJDK 25</td><td>Yes</td></tr> +</table> + +<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> + +<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 +<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> + +<!-- ============================================================ --> +<a name="quickstart"/> +<h2>1. Quick Start</h2> + +<p>Add the starter to your <code>pom.xml</code>:</p> + +<pre> +<dependency> + <groupId>org.apache.axis2</groupId> + <artifactId>axis2-spring-boot-starter</artifactId> + <version>2.0.1</version> +</dependency> + +<!-- Optional: OpenAPI/MCP endpoints --> +<dependency> + <groupId>org.apache.axis2</groupId> + <artifactId>axis2-openapi</artifactId> + <version>2.0.1</version> +</dependency> +</pre> + +<p>This single dependency replaces ~30 individual Axis2 dependencies +(<code>axis2-kernel</code>, <code>axis2-transport-http</code>, +<code>axis2-json</code>, <code>axis2-spring</code>, <code>axis2-jaxws</code>, +etc.) and eliminates the need for a hand-coded <code>Axis2WebAppInitializer</code> +or <code>OpenApiServlet</code> class.</p> + +<p>The starter auto-registers <code>AxisServlet</code> at <code>/services/*</code> +and — when <code>axis2-openapi</code> is on the classpath — registers the OpenAPI +servlet at <code>/openapi.json</code>, <code>/openapi.yaml</code>, +<code>/swagger-ui</code>, and <code>/openapi-mcp.json</code>.</p> + +<!-- ============================================================ --> +<a name="how_axisservlet_works"/> +<h2>2. How AxisServlet Works (SOAP vs JSON)</h2> + +<p><code>org.apache.axis2.transport.http.AxisServlet</code> is the single entry +point for <strong>all</strong> Axis2 service calls — SOAP and JSON-RPC alike. +The servlet itself is protocol-agnostic. It does not know or care whether the +incoming request is SOAP 1.1, SOAP 1.2, or JSON-RPC.</p> + +<p>The protocol handling is determined entirely by <code>axis2.xml</code>:</p> + +<table border="1"> +<tr><th>Component</th><th>SOAP</th><th>JSON-RPC</th></tr> +<tr> + <td><strong>Message Receivers</strong><br/>Parse the incoming payload and invoke the service method</td> + <td><code>RawXMLINOutMessageReceiver</code><br/><code>RawXMLINOnlyMessageReceiver</code><br/>(read SOAP XML envelopes)</td> + <td><code>JsonRpcMessageReceiver</code><br/><code>JsonInOnlyRPCMessageReceiver</code><br/>(read JSON-RPC <code>{"op":[{"arg0":{}}]}</code> envelope)</td> +</tr> +<tr> + <td><strong>Dispatchers</strong><br/>Route the request to the correct service and operation</td> + <td>Multi-handler chain:<br/><code>SOAPActionBasedDispatcher</code><br/><code>RequestURIBasedDispatcher</code><br/><code>SOAPMessageBodyBasedDispatcher</code><br/><code>HTTPLocationBasedDispatcher</code><br/>(routes by SOAPAction header, WS-Addressing, message body)</td> + <td>Single handler:<br/><code>JSONBasedDefaultDispatcher</code><br/>(routes by URL path only)</td> +</tr> +<tr> + <td><strong><code>enableJSONOnly</code></strong></td> + <td><code>false</code> (the default since Axis2 1.0, 2006)</td> + <td><code>true</code> (skips SOAP envelope processing)</td> +</tr> +</table> + +<p><strong>SOAP and JSON cannot coexist in a single deployment.</strong> +The message receivers and dispatchers are incompatible — a SOAP receiver cannot +parse JSON, and the JSON dispatcher cannot route by SOAPAction. This is not a +starter limitation; it is how Axis2's processing pipeline works.</p> + +<h3>AxisServlet Initialization Sequence</h3> + +<p>When AxisServlet starts (<code>init()</code>), it:</p> +<ol> +<li>Creates a <code>WarBasedAxisConfigurator</code></li> +<li>Locates the repository (<code>WEB-INF/</code>) using, in order: + <ol> + <li><code>axis2.repository.path</code> servlet init-parameter (if set explicitly)</li> + <li><code>axis2.repository.url</code> servlet init-parameter (if set explicitly)</li> + <li><code>ServletContext.getRealPath("/WEB-INF")</code> (automatic fallback)</li> + </ol> +</li> +<li>Reads <code>WEB-INF/conf/axis2.xml</code> to configure the engine</li> +<li>Scans <code>WEB-INF/services/*.aar</code> to deploy services</li> +<li>Scans <code>WEB-INF/modules/*.mar</code> to load modules (e.g., OpenAPI)</li> +<li>Stores the <code>ConfigurationContext</code> in the <code>ServletContext</code> + — this is how other servlets (OpenAPI, MCP) access the Axis2 engine</li> +</ol> + +<p>The starter sets <code>axis2.repository.path</code> eagerly at startup as a +workaround for WildFly VFS timing issues where <code>getRealPath()</code> can +return <code>null</code> during lazy servlet initialization.</p> + +<!-- ============================================================ --> +<a name="properties"/> +<h2>3. Configuration Properties</h2> + +<table border="1"> +<tr><th>Property</th><th>Default</th><th>Description</th></tr> +<tr><td><code>axis2.enabled</code></td><td><code>true</code></td> + <td>Set to <code>false</code> to disable Axis2 entirely (e.g., for server roles that don't serve web services)</td></tr> +<tr><td><code>axis2.mode</code></td><td><code>json</code></td> + <td><code>soap</code> or <code>json</code> — selects the built-in axis2.xml template (see Section 4)</td></tr> +<tr><td><code>axis2.services-path</code></td><td><code>/services</code></td> + <td>URL path for AxisServlet. Servlet is registered at <code>{services-path}/*</code></td></tr> +<tr><td><code>axis2.configuration-file</code></td><td><em>(empty)</em></td> + <td>Path to a custom axis2.xml. Overrides <code>axis2.mode</code>. Supports <code>classpath:</code> prefix</td></tr> +<tr><td><code>axis2.openapi.enabled</code></td><td><code>true</code></td> + <td>Register OpenAPI/MCP servlet (requires <code>axis2-openapi</code> on classpath)</td></tr> +<tr><td><code>axis2.openapi.paths</code></td><td><code>/openapi.json, /openapi.yaml, /swagger-ui, /openapi-mcp.json</code></td> + <td>URL paths for OpenAPI endpoints</td></tr> +</table> + +<!-- ============================================================ --> +<a name="soap_vs_json"/> +<h2>4. SOAP Mode vs JSON Mode</h2> + +<p>The starter ships two built-in <code>axis2.xml</code> templates, selected by +<code>axis2.mode</code>:</p> + +<h3>JSON mode (<code>axis2.mode=json</code>, the default)</h3> +<pre> +# application.properties +axis2.mode=json +</pre> +<p>Uses <code>JsonRpcMessageReceiver</code>, <code>JSONBasedDefaultDispatcher</code>, +and <code>enableJSONOnly=true</code>. Choose this for new services, MCP/AI integration, +and REST/OpenAPI consumers.</p> + +<h3>SOAP mode (<code>axis2.mode=soap</code>)</h3> +<pre> +# application.properties +axis2.mode=soap +</pre> +<p>Uses <code>RawXMLINOutMessageReceiver</code>, the full SOAP dispatcher stack, +and <code>enableJSONOnly=false</code>. Choose this for WSDL-first services, +WS-Security, WS-Addressing, or interop with .NET/PHP SOAP clients. This is the +classic Axis2 configuration that has been in use since version 1.0 (2006).</p> + +<h3>Custom axis2.xml</h3> +<pre> +# application.properties +axis2.configuration-file=classpath:my-axis2.xml +</pre> +<p>Overrides both built-in templates. Use this when you need custom phases, +modules, transport configurations, or any axis2.xml settings not covered by +the built-in templates.</p> + +<p>The starter only stages axis2.xml if <code>WEB-INF/conf/axis2.xml</code> +does not already exist. If your Maven build pre-stages it (e.g., via +<code>maven-antrun-plugin</code>), the starter will not overwrite it.</p> + +<!-- ============================================================ --> +<a name="what_autoconfigured"/> +<h2>5. What Gets Autoconfigured</h2> + +<table border="1"> +<tr><th>Component</th><th>What it replaces</th><th>Condition</th></tr> +<tr> + <td><code>AxisServlet</code> registration at <code>/services/*</code></td> + <td>Hand-coded <code>Axis2WebAppInitializer</code></td> + <td>Always (when <code>axis2.enabled=true</code>)</td> +</tr> +<tr> + <td><code>axis2.xml</code> staging (SOAP or JSON template)</td> + <td><code>axis2.xml</code> copy step in <code>maven-antrun-plugin</code></td> + <td>When <code>WEB-INF/conf/axis2.xml</code> does not already exist</td> +</tr> +<tr> + <td>OpenAPI/MCP servlet at <code>/openapi.json</code>, <code>/swagger-ui</code>, <code>/openapi-mcp.json</code></td> + <td>Hand-coded <code>OpenApiServlet</code> class</td> + <td>When <code>axis2-openapi</code> is on classpath and <code>axis2.openapi.enabled=true</code></td> +</tr> +</table> + +<!-- ============================================================ --> +<a name="what_stays"/> +<h2>6. What Stays in Your Application</h2> + +<p>The starter deliberately does <strong>not</strong> autoconfigure:</p> + +<ul> +<li><strong>Security</strong> — JWT authentication, mTLS, SAML, CSRF guard filter + chains are application-specific. The starter does not provide any + <code>SecurityFilterChain</code> beans.</li> +<li><strong>Service definitions</strong> — <code>services.xml</code> files inside + <code>.aar</code> archives define operation names, Spring bean names, + per-operation message receivers. These are service-specific.</li> +<li><strong><code>.aar</code> file creation</strong> — pre-built <code>.aar</code> files + must be staged in <code>WEB-INF/services/</code> by the Maven build + (<code>maven-antrun-plugin</code> or <code>axis2-aar-maven-plugin</code>).</li> +<li><strong><code>.mar</code> module staging</strong> — the OpenAPI module JAR must be + renamed to <code>.mar</code> and placed in <code>WEB-INF/modules/</code> by the + Maven build.</li> +<li><strong>Container-specific descriptors</strong> — WildFly's + <code>jboss-deployment-structure.xml</code>, <code>jboss-web.xml</code>, + <code>beans.xml</code>.</li> +<li><strong><code>@SpringBootApplication</code> class</strong> — the consuming app + still needs its main class extending <code>SpringBootServletInitializer</code>.</li> +</ul> + +<!-- ============================================================ --> +<a name="openapi_mcp"/> +<h2>7. OpenAPI and MCP Support</h2> + +<p>When <code>axis2-openapi</code> is on the classpath, the starter automatically +registers the OpenAPI servlet. No additional code is needed in the consuming app.</p> + +<p>The servlet provides:</p> +<ul> +<li><code>GET /openapi.json</code> — OpenAPI 3.0.1 specification</li> +<li><code>GET /openapi.yaml</code> — same in YAML format</li> +<li><code>GET /swagger-ui</code> — interactive Swagger UI</li> +<li><code>GET /openapi-mcp.json</code> — MCP tool catalog for AI assistants</li> +</ul> + +<p>See the <a href="json-rpc-mcp-guide.html">JSON-RPC MCP Integration Guide</a> +for details on the MCP catalog format, the Axis2 JSON-RPC envelope, and how +AI assistants discover and call Axis2 services.</p> + +<!-- ============================================================ --> +<a name="migration"/> +<h2>8. Migration from Manual Configuration</h2> + +<p>If you have an existing Axis2 + Spring Boot project with manual servlet +registration, follow these steps:</p> + +<ol> +<li><strong>Replace dependencies:</strong> Remove the ~30 individual Axis2 + dependencies and add the single <code>axis2-spring-boot-starter</code> + dependency.</li> +<li><strong>Delete <code>Axis2WebAppInitializer</code></strong> (or your + equivalent <code>ServletContextInitializer</code> that registers + <code>AxisServlet</code>). The starter handles this.</li> +<li><strong>Delete <code>OpenApiServlet.java</code></strong> if you have one. + The starter provides it when <code>axis2-openapi</code> is on the classpath.</li> +<li><strong>Set <code>axis2.mode</code></strong> in <code>application.properties</code>: + <code>json</code> for JSON-RPC services, <code>soap</code> for SOAP services.</li> +<li><strong>Keep your Maven build steps</strong> that create <code>.aar</code> files + and copy <code>.mar</code> modules — the starter does not replace these yet.</li> +<li><strong>Keep your security configuration</strong> — the starter does not touch + Spring Security.</li> +</ol> + +<p>For working reference implementations, see:</p> +<ul> +<li><code>modules/samples/userguide/src/userguide/springbootdemo-tomcat11/README.md</code></li> +<li><code>modules/samples/userguide/src/userguide/springbootdemo-wildfly/README.md</code></li> +</ul> + +<!-- ============================================================ --> +<a name="testing"/> +<h2>9. Tested Configurations</h2> + +<h3>JSON-RPC / MCP (axis2.mode=json)</h3> + +<p>The starter was tested with the <code>springbootdemo-tomcat11</code> sample +application, replacing ~30 individual Axis2 dependencies with the single +<code>axis2-spring-boot-starter</code> dependency and removing the hand-coded +<code>Axis2WebAppInitializer</code>. All endpoints confirmed working over +HTTPS/HTTP2 on Tomcat 11 with OpenJDK 25:</p> + +<table border="1"> +<tr><th>Endpoint</th><th>Result</th></tr> +<tr><td><code>GET /openapi.json</code></td><td>Pass — OpenAPI 3.0.1 spec returned</td></tr> +<tr><td><code>GET /openapi.yaml</code></td><td>Pass</td></tr> +<tr><td><code>GET /openapi-mcp.json</code></td><td>Pass — MCP tool catalog with full inputSchema</td></tr> +<tr><td><code>GET /swagger-ui</code></td><td>Pass — HTTP 200</td></tr> +<tr><td><code>POST loginService/doLogin</code></td><td>Pass — JWT token returned</td></tr> +<tr><td><code>POST FinancialBenchmarkService/portfolioVariance</code></td><td>Pass — SUCCESS, variance=0.03168</td></tr> +<tr><td><code>POST FinancialBenchmarkService/monteCarlo</code></td><td>Pass — SUCCESS, 10K simulations</td></tr> +<tr><td><code>POST FinancialBenchmarkService/scenarioAnalysis</code></td><td>Pass — SUCCESS</td></tr> +</table> + +<h3>SOAP (axis2.mode=soap)</h3> + +<p>The SOAP axis2.xml template was validated by unit tests confirming correct +message receivers (<code>RawXMLINOutMessageReceiver</code>, +<code>RawXMLINOnlyMessageReceiver</code>), the full SOAP dispatcher stack +(<code>SOAPActionBasedDispatcher</code>, <code>RequestURIBasedDispatcher</code>, +<code>SOAPMessageBodyBasedDispatcher</code>, <code>HTTPLocationBasedDispatcher</code>), +and <code>enableJSONOnly=false</code>. The SOAP template is based on the +Axis2 configuration that has been in production since version 1.0 (2006) and is +identical in structure to the configuration used by the legacy userguide +<code>example1</code> Echo service (<code>MyService.echo()</code> with +<code>RawXMLINOutMessageReceiver</code>).</p> + +<h3>Unit test suite</h3> + +<p>The starter includes 9 unit tests covering:</p> +<ul> +<li>Both axis2.xml templates exist on the classpath</li> +<li>JSON template correctness (enableJSONOnly=true, JSONBasedDefaultDispatcher, no SOAP receivers)</li> +<li>SOAP template correctness (enableJSONOnly=false, RawXMLINOut receivers, full SOAP dispatcher stack, no JSON dispatcher)</li> +<li>Default property values</li> +<li>AutoConfiguration.imports contains all required classes</li> +</ul> + +<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> + +</body> +</html> diff --git a/src/site/xdoc/docs/toc.xml b/src/site/xdoc/docs/toc.xml index 582879d5f6..878dafca36 100644 --- a/src/site/xdoc/docs/toc.xml +++ b/src/site/xdoc/docs/toc.xml @@ -72,6 +72,20 @@ Administrator's Guide</a></li> <li><a href="jaxws-guide.html">JAXWS Guide</a></li> <li><a href="pojoguide.html">POJO Guide</a></li> <li><a href="spring.html">Spring Guide</a></li> +<li><a href="spring-boot-starter.html">Spring Boot Starter</a> +<ul> +<li>12a.0 <a href="spring-boot-starter.html#deployment_model">Deployment Model (WAR Only — Embedded Not Supported)</a></li> +<li>12a.1 <a href="spring-boot-starter.html#quickstart">Quick Start (Single Dependency)</a></li> +<li>12a.2 <a href="spring-boot-starter.html#how_axisservlet_works">How AxisServlet Works (SOAP vs JSON)</a></li> +<li>12a.3 <a href="spring-boot-starter.html#properties">Configuration Properties</a></li> +<li>12a.4 <a href="spring-boot-starter.html#soap_vs_json">SOAP Mode vs JSON Mode</a></li> +<li>12a.5 <a href="spring-boot-starter.html#what_autoconfigured">What Gets Autoconfigured</a></li> +<li>12a.6 <a href="spring-boot-starter.html#what_stays">What Stays in Your Application</a></li> +<li>12a.7 <a href="spring-boot-starter.html#openapi_mcp">OpenAPI and MCP Support</a></li> +<li>12a.8 <a href="spring-boot-starter.html#migration">Migration from Manual Configuration</a></li> +<li>12a.9 <a href="spring-boot-starter.html#containers">Container Support</a></li> +</ul> +</li> <li><a href="modules.html">ModulesGuide</a></li> <li><a href="clustering-guide.html">Clustering Guide</a></li> <li>ADB Data Binding
