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 9fee3fca613f994c4b70691931d31c0acd1a4276 Author: Robert Lazarski <[email protected]> AuthorDate: Sat Apr 4 15:23:11 2026 -1000 Wire axis2-openapi module into springbootdemo samples Adds the missing HTTP routing layer so the OpenAPI endpoints are reachable in both the Tomcat 11 and WildFly springbootdemo deployments. Changes per sample: - pom.xml: add axis2-openapi dependency - axis2.xml: add <module ref="openapi"/> - OpenApiController: new Spring @Controller routing GET /openapi.json, /openapi.yaml, /swagger-ui to SwaggerUIHandler; retrieves ConfigurationContext from ServletContext via AxisServlet.CONFIGURATION_CONTEXT - Axis2Application: add @Order(2) OpenAPI filter chain that allows unauthenticated GET requests to the three OpenAPI paths, bypassing the HTTPPostOnlyRejectionFilter and JWT auth that protects /services/*; bump login chain from @Order(1) to @Order(3) accordingly Co-Authored-By: Claude Sonnet 4.6 <[email protected]> --- .../src/userguide/springbootdemo-tomcat11/pom.xml | 5 ++ .../resources-axis2/conf/axis2.xml | 3 + .../userguide/springboot/Axis2Application.java | 27 +++++- .../configuration/OpenApiController.java | 99 ++++++++++++++++++++++ .../userguide/src/userguide/springbootdemo/pom.xml | 5 ++ .../springbootdemo/resources-axis2/conf/axis2.xml | 3 + .../userguide/springboot/Axis2Application.java | 27 +++++- .../configuration/OpenApiController.java | 99 ++++++++++++++++++++++ 8 files changed, 264 insertions(+), 4 deletions(-) diff --git a/modules/samples/userguide/src/userguide/springbootdemo-tomcat11/pom.xml b/modules/samples/userguide/src/userguide/springbootdemo-tomcat11/pom.xml index 8cc7a00112..c1de1b099e 100644 --- a/modules/samples/userguide/src/userguide/springbootdemo-tomcat11/pom.xml +++ b/modules/samples/userguide/src/userguide/springbootdemo-tomcat11/pom.xml @@ -230,6 +230,11 @@ <artifactId>axis2-jaxws</artifactId> <version>2.0.1-SNAPSHOT</version> </dependency> + <dependency> + <groupId>org.apache.axis2</groupId> + <artifactId>axis2-openapi</artifactId> + <version>2.0.1-SNAPSHOT</version> + </dependency> <dependency> <groupId>org.apache.neethi</groupId> <artifactId>neethi</artifactId> diff --git a/modules/samples/userguide/src/userguide/springbootdemo-tomcat11/resources-axis2/conf/axis2.xml b/modules/samples/userguide/src/userguide/springbootdemo-tomcat11/resources-axis2/conf/axis2.xml index 5ab7e1e1ca..b0f955b614 100644 --- a/modules/samples/userguide/src/userguide/springbootdemo-tomcat11/resources-axis2/conf/axis2.xml +++ b/modules/samples/userguide/src/userguide/springbootdemo-tomcat11/resources-axis2/conf/axis2.xml @@ -258,6 +258,9 @@ <!-- 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>--> diff --git a/modules/samples/userguide/src/userguide/springbootdemo-tomcat11/src/main/java/userguide/springboot/Axis2Application.java b/modules/samples/userguide/src/userguide/springbootdemo-tomcat11/src/main/java/userguide/springboot/Axis2Application.java index 471f9b8c18..838d1bf3b6 100644 --- a/modules/samples/userguide/src/userguide/springbootdemo-tomcat11/src/main/java/userguide/springboot/Axis2Application.java +++ b/modules/samples/userguide/src/userguide/springbootdemo-tomcat11/src/main/java/userguide/springboot/Axis2Application.java @@ -347,11 +347,34 @@ public class Axis2Application extends SpringBootServletInitializer { return headerFilter; } - // these two chains are a binary choice. + // OpenAPI documentation endpoints — GET requests, no auth required + class OpenApiRequestMatcher implements RequestMatcher { + private static final String[] OPENAPI_PATHS = {"/openapi.json", "/openapi.yaml", "/swagger-ui"}; + + @Override + public boolean matches(HttpServletRequest request) { + String uri = request.getRequestURI(); + for (String path : OPENAPI_PATHS) { + if (uri.equals(path) || uri.startsWith(path + "/")) { + return true; + } + } + return false; + } + } + + @Bean(name = "springSecurityFilterChainOpenApi") + @Order(2) + public SecurityFilterChain springSecurityFilterChainOpenApi() throws Exception { + // Only header filter — no POST restriction, no JWT, no login processing + return new DefaultSecurityFilterChain(new OpenApiRequestMatcher(), headerWriterFilter()); + } + + // these two chains are a binary choice. // A login url will match, otherwise invoke jwtAuthenticationFilter @Bean(name = "springSecurityFilterChainLogin") - @Order(1) + @Order(3) public SecurityFilterChain springSecurityFilterChainLogin() throws ServletException, Exception { String logPrefix = "GenericAccessDecisionManager.springSecurityFilterChain , "; logger.debug(logPrefix + "inside main filter config ..."); diff --git a/modules/samples/userguide/src/userguide/springbootdemo-tomcat11/src/main/java/userguide/springboot/configuration/OpenApiController.java b/modules/samples/userguide/src/userguide/springbootdemo-tomcat11/src/main/java/userguide/springboot/configuration/OpenApiController.java new file mode 100644 index 0000000000..1518b36c5c --- /dev/null +++ b/modules/samples/userguide/src/userguide/springbootdemo-tomcat11/src/main/java/userguide/springboot/configuration/OpenApiController.java @@ -0,0 +1,99 @@ +/* + * 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 userguide.springboot.configuration; + +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +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.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +/** + * Routes OpenAPI documentation requests to the Axis2 OpenAPI module handlers. + * + * The axis2-openapi module stores its handlers in ConfigurationContext during + * module init. AxisServlet stores ConfigurationContext in ServletContext under + * the key AxisServlet.CONFIGURATION_CONTEXT ("CONFIGURATION_CONTEXT"). This + * controller bridges Spring MVC routing to those handlers, enabling the standard + * OpenAPI endpoints alongside the Axis2 /services/* path. + * + * Endpoints served: + * GET /openapi.json - OpenAPI 3.0.1 specification (JSON) + * GET /openapi.yaml - OpenAPI 3.0.1 specification (YAML) + * GET /swagger-ui - Interactive Swagger UI documentation page + */ +@Controller +public class OpenApiController { + + private static final Log log = LogFactory.getLog(OpenApiController.class); + + @Autowired + private ServletContext servletContext; + + private SwaggerUIHandler getHandler() { + ConfigurationContext configContext = (ConfigurationContext) + servletContext.getAttribute(AxisServlet.CONFIGURATION_CONTEXT); + if (configContext == null) { + log.warn("AxisServlet ConfigurationContext not found in ServletContext — AxisServlet may not have started yet"); + return null; + } + SwaggerUIHandler handler = OpenApiModule.getSwaggerUIHandler(configContext); + if (handler == null) { + log.warn("OpenAPI module not initialized — ensure axis2-openapi is on the classpath and <module ref=\"openapi\"/> is in axis2.xml"); + } + return handler; + } + + @GetMapping("/openapi.json") + public void openApiJson(HttpServletRequest request, HttpServletResponse response) throws Exception { + SwaggerUIHandler handler = getHandler(); + if (handler == null) { + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "OpenAPI module not available"); + return; + } + handler.handleOpenApiJsonRequest(request, response); + } + + @GetMapping("/openapi.yaml") + public void openApiYaml(HttpServletRequest request, HttpServletResponse response) throws Exception { + SwaggerUIHandler handler = getHandler(); + if (handler == null) { + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "OpenAPI module not available"); + return; + } + handler.handleOpenApiYamlRequest(request, response); + } + + @GetMapping("/swagger-ui") + public void swaggerUi(HttpServletRequest request, HttpServletResponse response) throws Exception { + SwaggerUIHandler handler = getHandler(); + if (handler == null) { + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "OpenAPI module not available"); + return; + } + handler.handleSwaggerUIRequest(request, response); + } +} diff --git a/modules/samples/userguide/src/userguide/springbootdemo/pom.xml b/modules/samples/userguide/src/userguide/springbootdemo/pom.xml index 8cc7a00112..c1de1b099e 100644 --- a/modules/samples/userguide/src/userguide/springbootdemo/pom.xml +++ b/modules/samples/userguide/src/userguide/springbootdemo/pom.xml @@ -230,6 +230,11 @@ <artifactId>axis2-jaxws</artifactId> <version>2.0.1-SNAPSHOT</version> </dependency> + <dependency> + <groupId>org.apache.axis2</groupId> + <artifactId>axis2-openapi</artifactId> + <version>2.0.1-SNAPSHOT</version> + </dependency> <dependency> <groupId>org.apache.neethi</groupId> <artifactId>neethi</artifactId> diff --git a/modules/samples/userguide/src/userguide/springbootdemo/resources-axis2/conf/axis2.xml b/modules/samples/userguide/src/userguide/springbootdemo/resources-axis2/conf/axis2.xml index 5ab7e1e1ca..b0f955b614 100644 --- a/modules/samples/userguide/src/userguide/springbootdemo/resources-axis2/conf/axis2.xml +++ b/modules/samples/userguide/src/userguide/springbootdemo/resources-axis2/conf/axis2.xml @@ -258,6 +258,9 @@ <!-- 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>--> diff --git a/modules/samples/userguide/src/userguide/springbootdemo/src/main/java/userguide/springboot/Axis2Application.java b/modules/samples/userguide/src/userguide/springbootdemo/src/main/java/userguide/springboot/Axis2Application.java index 73cb30c642..c6e442891c 100644 --- a/modules/samples/userguide/src/userguide/springbootdemo/src/main/java/userguide/springboot/Axis2Application.java +++ b/modules/samples/userguide/src/userguide/springbootdemo/src/main/java/userguide/springboot/Axis2Application.java @@ -341,11 +341,34 @@ public class Axis2Application extends SpringBootServletInitializer { return headerFilter; } - // these two chains are a binary choice. + // OpenAPI documentation endpoints — GET requests, no auth required + class OpenApiRequestMatcher implements RequestMatcher { + private static final String[] OPENAPI_PATHS = {"/openapi.json", "/openapi.yaml", "/swagger-ui"}; + + @Override + public boolean matches(HttpServletRequest request) { + String uri = request.getRequestURI(); + for (String path : OPENAPI_PATHS) { + if (uri.equals(path) || uri.startsWith(path + "/")) { + return true; + } + } + return false; + } + } + + @Bean(name = "springSecurityFilterChainOpenApi") + @Order(2) + public SecurityFilterChain springSecurityFilterChainOpenApi() throws Exception { + // Only header filter — no POST restriction, no JWT, no login processing + return new DefaultSecurityFilterChain(new OpenApiRequestMatcher(), headerWriterFilter()); + } + + // these two chains are a binary choice. // A login url will match, otherwise invoke jwtAuthenticationFilter @Bean(name = "springSecurityFilterChainLogin") - @Order(1) + @Order(3) public SecurityFilterChain springSecurityFilterChainLogin() throws ServletException, Exception { String logPrefix = "GenericAccessDecisionManager.springSecurityFilterChain , "; logger.debug(logPrefix + "inside main filter config ..."); diff --git a/modules/samples/userguide/src/userguide/springbootdemo/src/main/java/userguide/springboot/configuration/OpenApiController.java b/modules/samples/userguide/src/userguide/springbootdemo/src/main/java/userguide/springboot/configuration/OpenApiController.java new file mode 100644 index 0000000000..1518b36c5c --- /dev/null +++ b/modules/samples/userguide/src/userguide/springbootdemo/src/main/java/userguide/springboot/configuration/OpenApiController.java @@ -0,0 +1,99 @@ +/* + * 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 userguide.springboot.configuration; + +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +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.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +/** + * Routes OpenAPI documentation requests to the Axis2 OpenAPI module handlers. + * + * The axis2-openapi module stores its handlers in ConfigurationContext during + * module init. AxisServlet stores ConfigurationContext in ServletContext under + * the key AxisServlet.CONFIGURATION_CONTEXT ("CONFIGURATION_CONTEXT"). This + * controller bridges Spring MVC routing to those handlers, enabling the standard + * OpenAPI endpoints alongside the Axis2 /services/* path. + * + * Endpoints served: + * GET /openapi.json - OpenAPI 3.0.1 specification (JSON) + * GET /openapi.yaml - OpenAPI 3.0.1 specification (YAML) + * GET /swagger-ui - Interactive Swagger UI documentation page + */ +@Controller +public class OpenApiController { + + private static final Log log = LogFactory.getLog(OpenApiController.class); + + @Autowired + private ServletContext servletContext; + + private SwaggerUIHandler getHandler() { + ConfigurationContext configContext = (ConfigurationContext) + servletContext.getAttribute(AxisServlet.CONFIGURATION_CONTEXT); + if (configContext == null) { + log.warn("AxisServlet ConfigurationContext not found in ServletContext — AxisServlet may not have started yet"); + return null; + } + SwaggerUIHandler handler = OpenApiModule.getSwaggerUIHandler(configContext); + if (handler == null) { + log.warn("OpenAPI module not initialized — ensure axis2-openapi is on the classpath and <module ref=\"openapi\"/> is in axis2.xml"); + } + return handler; + } + + @GetMapping("/openapi.json") + public void openApiJson(HttpServletRequest request, HttpServletResponse response) throws Exception { + SwaggerUIHandler handler = getHandler(); + if (handler == null) { + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "OpenAPI module not available"); + return; + } + handler.handleOpenApiJsonRequest(request, response); + } + + @GetMapping("/openapi.yaml") + public void openApiYaml(HttpServletRequest request, HttpServletResponse response) throws Exception { + SwaggerUIHandler handler = getHandler(); + if (handler == null) { + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "OpenAPI module not available"); + return; + } + handler.handleOpenApiYamlRequest(request, response); + } + + @GetMapping("/swagger-ui") + public void swaggerUi(HttpServletRequest request, HttpServletResponse response) throws Exception { + SwaggerUIHandler handler = getHandler(); + if (handler == null) { + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "OpenAPI module not available"); + return; + } + handler.handleSwaggerUIRequest(request, response); + } +}
