Repository: piranhacloud/piranha Branch: current Commit: 5c473674ea66 Files: 1176 Total size: 4.3 MB Directory structure: gitextract_k7aup1uh/ ├── .github/ │ ├── FUNDING.yml │ ├── dependabot.yml │ └── workflows/ │ ├── current.yml │ ├── docs.yml │ ├── experimental.yml │ ├── pr.yml │ ├── release.yml │ ├── stale.yml │ ├── tck-coreprofile.yml │ └── trigger.yml ├── .gitignore ├── CODE_CONVENTIONS.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── RELEASE.md ├── SECURITY.md ├── arquillian/ │ ├── managed/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ ├── cloud/ │ │ │ │ │ └── piranha/ │ │ │ │ │ └── arquillian/ │ │ │ │ │ └── managed/ │ │ │ │ │ ├── ManagedPiranhaContainer.java │ │ │ │ │ ├── ManagedPiranhaContainerConfiguration.java │ │ │ │ │ └── ManagedPiranhaContainerExtension.java │ │ │ │ └── module-info.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── services/ │ │ │ └── org.jboss.arquillian.core.spi.LoadableExtension │ │ └── test/ │ │ ├── java/ │ │ │ └── cloud/ │ │ │ └── piranha/ │ │ │ └── arquillian/ │ │ │ └── managed/ │ │ │ ├── ManagedPiranhaContainerConfigurationTest.java │ │ │ ├── ManagedPiranhaContainerExtensionTest.java │ │ │ └── ManagedPiranhaContainerTest.java │ │ └── webapp/ │ │ └── test/ │ │ └── WEB-INF/ │ │ └── web.xml │ ├── pom.xml │ └── server/ │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── arquillian/ │ │ │ └── server/ │ │ │ ├── PiranhaServerDeployableContainer.java │ │ │ └── PiranhaServerLoadableExtension.java │ │ └── module-info.java │ └── resources/ │ └── META-INF/ │ └── services/ │ └── org.jboss.arquillian.core.spi.LoadableExtension ├── bom/ │ └── pom.xml ├── core/ │ ├── api/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── core/ │ │ │ │ └── api/ │ │ │ │ ├── AnnotationInfo.java │ │ │ │ ├── AnnotationManager.java │ │ │ │ ├── AsyncDispatcher.java │ │ │ │ ├── AsyncManager.java │ │ │ │ ├── AttributeManager.java │ │ │ │ ├── AuthenticatedIdentity.java │ │ │ │ ├── CurrentRequestHolder.java │ │ │ │ ├── DispatcherManager.java │ │ │ │ ├── ErrorPageManager.java │ │ │ │ ├── FilterEnvironment.java │ │ │ │ ├── FilterMapping.java │ │ │ │ ├── FilterPriority.java │ │ │ │ ├── HandlesTypesManager.java │ │ │ │ ├── HttpHeader.java │ │ │ │ ├── HttpHeaderManager.java │ │ │ │ ├── HttpSessionManager.java │ │ │ │ ├── JspManager.java │ │ │ │ ├── LocaleEncodingManager.java │ │ │ │ ├── ModuleLayerProcessor.java │ │ │ │ ├── MultiPartManager.java │ │ │ │ ├── ObjectInstanceManager.java │ │ │ │ ├── Piranha.java │ │ │ │ ├── PiranhaBuilder.java │ │ │ │ ├── PiranhaConfiguration.java │ │ │ │ ├── SecurityConstraint.java │ │ │ │ ├── SecurityManager.java │ │ │ │ ├── SecurityRoleReference.java │ │ │ │ ├── SecurityWebResourceCollection.java │ │ │ │ ├── ServletEnvironment.java │ │ │ │ ├── ServletInvocation.java │ │ │ │ ├── ServletRequestManager.java │ │ │ │ ├── WebApplication.java │ │ │ │ ├── WebApplicationClassLoader.java │ │ │ │ ├── WebApplicationExtension.java │ │ │ │ ├── WebApplicationExtensionContext.java │ │ │ │ ├── WebApplicationInputStream.java │ │ │ │ ├── WebApplicationManager.java │ │ │ │ ├── WebApplicationOutputStream.java │ │ │ │ ├── WebApplicationPrintWriter.java │ │ │ │ ├── WebApplicationRequest.java │ │ │ │ ├── WebApplicationRequestMapper.java │ │ │ │ ├── WebApplicationRequestMapping.java │ │ │ │ ├── WebApplicationResponse.java │ │ │ │ ├── WebApplicationServer.java │ │ │ │ ├── WebApplicationServerRequestMapper.java │ │ │ │ └── WelcomeFileManager.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── core/ │ │ └── api/ │ │ └── AttributeManagerTest.java │ ├── impl/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── core/ │ │ │ │ └── impl/ │ │ │ │ ├── AsyncHttpDispatchWrapper.java │ │ │ │ ├── AsyncNonHttpDispatchWrapper.java │ │ │ │ ├── CookieParser.java │ │ │ │ ├── DefaultAnnotationManager.java │ │ │ │ ├── DefaultAsyncContext.java │ │ │ │ ├── DefaultAsyncDispatcher.java │ │ │ │ ├── DefaultAsyncManager.java │ │ │ │ ├── DefaultAttributeManager.java │ │ │ │ ├── DefaultAuthenticatedIdentity.java │ │ │ │ ├── DefaultCurrentRequestHolder.java │ │ │ │ ├── DefaultDispatcherManager.java │ │ │ │ ├── DefaultErrorPageManager.java │ │ │ │ ├── DefaultFilterChain.java │ │ │ │ ├── DefaultFilterEnvironment.java │ │ │ │ ├── DefaultFilterMapping.java │ │ │ │ ├── DefaultHttpHeader.java │ │ │ │ ├── DefaultHttpHeaderManager.java │ │ │ │ ├── DefaultHttpServletMapping.java │ │ │ │ ├── DefaultHttpSession.java │ │ │ │ ├── DefaultHttpSessionManager.java │ │ │ │ ├── DefaultInvocationFinder.java │ │ │ │ ├── DefaultJspConfigDescriptor.java │ │ │ │ ├── DefaultJspManager.java │ │ │ │ ├── DefaultLocaleEncodingManager.java │ │ │ │ ├── DefaultModuleFinder.java │ │ │ │ ├── DefaultModuleLayerProcessor.java │ │ │ │ ├── DefaultModuleReader.java │ │ │ │ ├── DefaultModuleReference.java │ │ │ │ ├── DefaultMultiPartManager.java │ │ │ │ ├── DefaultNamedRequestDispatcher.java │ │ │ │ ├── DefaultObjectInstanceManager.java │ │ │ │ ├── DefaultPiranha.java │ │ │ │ ├── DefaultPiranhaBuilder.java │ │ │ │ ├── DefaultPiranhaConfiguration.java │ │ │ │ ├── DefaultPushBuilder.java │ │ │ │ ├── DefaultSecurityManager.java │ │ │ │ ├── DefaultServlet.java │ │ │ │ ├── DefaultServletConnection.java │ │ │ │ ├── DefaultServletEnvironment.java │ │ │ │ ├── DefaultServletInvocation.java │ │ │ │ ├── DefaultServletRequestDispatcher.java │ │ │ │ ├── DefaultServletRequestManager.java │ │ │ │ ├── DefaultTaglibDescriptor.java │ │ │ │ ├── DefaultWebApplication.java │ │ │ │ ├── DefaultWebApplicationBuilder.java │ │ │ │ ├── DefaultWebApplicationClassLoader.java │ │ │ │ ├── DefaultWebApplicationExtensionContext.java │ │ │ │ ├── DefaultWebApplicationInputStream.java │ │ │ │ ├── DefaultWebApplicationManager.java │ │ │ │ ├── DefaultWebApplicationOutputStream.java │ │ │ │ ├── DefaultWebApplicationPrintWriter.java │ │ │ │ ├── DefaultWebApplicationRequest.java │ │ │ │ ├── DefaultWebApplicationRequestBuilder.java │ │ │ │ ├── DefaultWebApplicationRequestMapper.java │ │ │ │ ├── DefaultWebApplicationRequestMapping.java │ │ │ │ ├── DefaultWebApplicationResponse.java │ │ │ │ ├── DefaultWebApplicationResponseBuilder.java │ │ │ │ ├── DefaultWebConnection.java │ │ │ │ └── WarFileExtractor.java │ │ │ └── module-info.java │ │ └── test/ │ │ ├── java/ │ │ │ └── cloud/ │ │ │ └── piranha/ │ │ │ └── core/ │ │ │ └── impl/ │ │ │ ├── AsyncContextTest.java │ │ │ ├── CookieParserTest.java │ │ │ ├── DefaultAnnotationManagerTest.java │ │ │ ├── DefaultErrorPageManagerTest.java │ │ │ ├── DefaultFilterEnvironmentTest.java │ │ │ ├── DefaultHttpHeaderManagerTest.java │ │ │ ├── DefaultPiranhaConfigurationTest.java │ │ │ ├── DefaultPushBuilderTest.java │ │ │ ├── DefaultSecurityManagerTest.java │ │ │ ├── DefaultServletEnvironmentTest.java │ │ │ ├── DefaultWebApplicationBuilderTest.java │ │ │ ├── DefaultWebApplicationClassLoaderTest.java │ │ │ ├── DefaultWebApplicationExtensionContextTest.java │ │ │ ├── DefaultWebApplicationManagerTest.java │ │ │ ├── DefaultWebApplicationOutputStreamTest.java │ │ │ ├── DefaultWebApplicationRequestMapperTest.java │ │ │ ├── DefaultWebApplicationRequestMappingTest.java │ │ │ ├── DefaultWebApplicationRequestTest.java │ │ │ ├── DefaultWebApplicationResponseTest.java │ │ │ ├── DefaultWebApplicationTest.java │ │ │ ├── DefaultWebConnectionTest.java │ │ │ ├── FilterTest.java │ │ │ ├── HttpSessionTest.java │ │ │ ├── ReadListenerTest.java │ │ │ ├── RequestDispatcherTest.java │ │ │ ├── ServletContextAttributeListenerTest.java │ │ │ ├── ServletInputStreamTest.java │ │ │ ├── ServletRegistrationTest.java │ │ │ ├── ServletRequestAttributeListenerTest.java │ │ │ ├── ServletTest.java │ │ │ ├── TestChat1Servlet.java │ │ │ ├── TestChat2Servlet.java │ │ │ ├── TestEcho1Servlet.java │ │ │ ├── TestEcho2Servlet.java │ │ │ ├── TestHttpServerRequest.java │ │ │ ├── TestHttpServerResponse.java │ │ │ ├── TestIOExceptionServlet.java │ │ │ ├── TestInclude2Servlet.java │ │ │ ├── TestInclude3Servlet.java │ │ │ ├── TestInclude4Servlet.java │ │ │ ├── TestIncludeServlet.java │ │ │ ├── TestRuntimeExceptionServlet.java │ │ │ ├── TestSnoopServlet.java │ │ │ ├── TestWebApplicationRequest.java │ │ │ ├── TestWebApplicationResponse.java │ │ │ └── WriteListenerTest.java │ │ └── webapp/ │ │ └── resourcepaths/ │ │ ├── catalog/ │ │ │ ├── index.html │ │ │ ├── offers/ │ │ │ │ ├── books.html │ │ │ │ └── music.html │ │ │ └── products.html │ │ ├── customer/ │ │ │ └── login.jsp │ │ └── welcome.html │ └── pom.xml ├── debug/ │ ├── modules.sh │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ ├── cloud/ │ │ └── piranha/ │ │ └── debug/ │ │ └── Debug.java │ └── module-info.java ├── dist/ │ ├── coreprofile/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── dist/ │ │ │ │ └── coreprofile/ │ │ │ │ └── CoreProfilePiranhaMain.java │ │ │ └── module-info.java │ │ └── site/ │ │ ├── markdown/ │ │ │ ├── create_a_json_rest_service.md │ │ │ ├── create_a_rest_service.md │ │ │ ├── debugging_a_rest_service_with_netbeans.md │ │ │ ├── debugging_a_rest_service_with_vscode.md │ │ │ ├── index.md │ │ │ ├── testing_with_junit5_and_arquillian.md │ │ │ └── using_project_crac.md │ │ └── site.xml │ ├── isolated/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── assembly/ │ │ │ │ └── zip.xml │ │ │ ├── docker/ │ │ │ │ └── Dockerfile │ │ │ ├── java/ │ │ │ │ ├── cloud/ │ │ │ │ │ └── piranha/ │ │ │ │ │ └── dist/ │ │ │ │ │ └── isolated/ │ │ │ │ │ └── IsolatedPiranha.java │ │ │ │ └── module-info.java │ │ │ └── zip/ │ │ │ ├── etc/ │ │ │ │ ├── keystore.jks │ │ │ │ └── logging.properties │ │ │ ├── tmp/ │ │ │ │ └── README │ │ │ └── webapps/ │ │ │ └── README │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── dist/ │ │ └── isolated/ │ │ └── IsolatedPiranhaIT.java │ ├── micro/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── docker/ │ │ │ │ └── Dockerfile │ │ │ ├── java/ │ │ │ │ ├── cloud/ │ │ │ │ │ └── piranha/ │ │ │ │ │ └── dist/ │ │ │ │ │ └── micro/ │ │ │ │ │ ├── MicroBootstrap.java │ │ │ │ │ └── package-info.java │ │ │ │ └── module-info.java │ │ │ └── javadoc/ │ │ │ └── cloud.piranha.dist.micro/ │ │ │ └── cloud/ │ │ │ └── piranha/ │ │ │ └── dist/ │ │ │ └── micro/ │ │ │ └── doc-files/ │ │ │ └── request-response.pptx │ │ ├── site/ │ │ │ ├── markdown/ │ │ │ │ ├── create_a_hello_world_web_application.md │ │ │ │ └── index.md │ │ │ └── site.xml │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── dist/ │ │ └── micro/ │ │ └── MicroPiranhaIT.java │ ├── microprofile/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── docker/ │ │ │ └── Dockerfile │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── dist/ │ │ │ └── microprofile/ │ │ │ └── MicroProfilePiranhaMain.java │ │ └── module-info.java │ ├── platform/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── assembly/ │ │ │ │ └── zip.xml │ │ │ ├── docker/ │ │ │ │ └── Dockerfile │ │ │ ├── java/ │ │ │ │ ├── cloud/ │ │ │ │ │ └── piranha/ │ │ │ │ │ └── dist/ │ │ │ │ │ └── platform/ │ │ │ │ │ ├── PlatformPiranhaMain.java │ │ │ │ │ └── PlatformWebApplication.java │ │ │ │ └── module-info.java │ │ │ └── zip/ │ │ │ ├── etc/ │ │ │ │ ├── keystore.jks │ │ │ │ ├── logging.properties │ │ │ │ └── truststore.jks │ │ │ ├── tmp/ │ │ │ │ └── README │ │ │ └── webapps/ │ │ │ └── README │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── dist/ │ │ └── platform/ │ │ ├── PlatformPiranhaBuilderTest.java │ │ └── PlatformPiranhaIT.java │ ├── pom.xml │ ├── server/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── assembly/ │ │ │ │ └── zip.xml │ │ │ ├── docker/ │ │ │ │ └── Dockerfile │ │ │ ├── java/ │ │ │ │ ├── cloud/ │ │ │ │ │ └── piranha/ │ │ │ │ │ └── dist/ │ │ │ │ │ └── server/ │ │ │ │ │ ├── ServerPiranhaMain.java │ │ │ │ │ └── ServerWebApplication.java │ │ │ │ └── module-info.java │ │ │ └── zip/ │ │ │ ├── etc/ │ │ │ │ ├── keystore.jks │ │ │ │ ├── logging.properties │ │ │ │ └── truststore.jks │ │ │ ├── tmp/ │ │ │ │ └── README │ │ │ └── webapps/ │ │ │ └── README │ │ ├── site/ │ │ │ ├── markdown/ │ │ │ │ ├── create_a_hello_world_web_application.md │ │ │ │ └── index.md │ │ │ └── site.xml │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── dist/ │ │ └── server/ │ │ ├── ServerPiranhaBuilderTest.java │ │ └── ServerPiranhaIT.java │ ├── servlet/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── docker/ │ │ │ │ └── Dockerfile │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── dist/ │ │ │ │ └── servlet/ │ │ │ │ └── ServletPiranhaMain.java │ │ │ └── module-info.java │ │ └── site/ │ │ ├── markdown/ │ │ │ ├── create_a_hello_world_web_application.md │ │ │ ├── create_a_jakarta_pages_application.md │ │ │ ├── create_a_websocket_application.md │ │ │ ├── index.md │ │ │ └── run_a_web_application_on_crac.md │ │ └── site.xml │ └── webprofile/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── docker/ │ │ │ └── Dockerfile │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── dist/ │ │ │ └── webprofile/ │ │ │ └── WebProfilePiranhaMain.java │ │ └── module-info.java │ └── site/ │ ├── markdown/ │ │ ├── create_a_faces_application.md │ │ ├── create_a_hello_world_application.md │ │ ├── create_a_jakarta_rest_service.md │ │ ├── create_a_pages_application.md │ │ ├── index.md │ │ ├── testing_with_junit5_and_playwright.md │ │ └── testing_with_our_container_image.md │ └── site.xml ├── docker/ │ ├── coreprofile/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── docker/ │ │ │ └── Dockerfile │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── docker/ │ │ └── coreprofile/ │ │ └── CoreProfileIT.java │ └── pom.xml ├── embedded/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── embedded/ │ │ │ │ ├── EmbeddedPiranha.java │ │ │ │ ├── EmbeddedPiranhaBuilder.java │ │ │ │ ├── EmbeddedRequest.java │ │ │ │ ├── EmbeddedRequestBuilder.java │ │ │ │ ├── EmbeddedResponse.java │ │ │ │ ├── EmbeddedResponseBuilder.java │ │ │ │ └── package-info.java │ │ │ └── module-info.java │ │ └── javadoc/ │ │ └── cloud.piranha.embedded/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── embedded/ │ │ └── doc-files/ │ │ └── request-response.pptx │ ├── site/ │ │ ├── markdown/ │ │ │ ├── create_a_hello_world_web_application.md │ │ │ ├── create_a_piranha_embedded_graalvm_application.md │ │ │ ├── create_an_embedded_jlink_application.md │ │ │ ├── index.md │ │ │ └── running_piranha_embedded_with_spring_boot.md │ │ └── site.xml │ └── test/ │ └── java/ │ └── cloud/ │ └── piranha/ │ └── embedded/ │ ├── EmbeddedPiranhaBuilderTest.java │ ├── EmbeddedPiranhaTest.java │ ├── EmbeddedRequestBuilderTest.java │ ├── EmbeddedRequestTest.java │ └── EmbeddedResponseBuilderTest.java ├── extension/ │ ├── angus/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── angus/ │ │ │ └── AngusExtension.java │ │ └── module-info.java │ ├── annotationscan/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── annotationscan/ │ │ │ │ ├── AnnotationScanExtension.java │ │ │ │ ├── AnnotationScanInitializer.java │ │ │ │ └── internal/ │ │ │ │ ├── InternalAnnotationScanAnnotationInfo.java │ │ │ │ └── InternalAnnotationScanAnnotationManager.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── extension/ │ │ └── annotationscan/ │ │ ├── AnnotationScanExtensionTest.java │ │ ├── TestAnnotation.java │ │ ├── TestServlet.java │ │ ├── TestWithHandlesTypesInitializer.java │ │ └── internal/ │ │ └── InternalAnnotationScanAnnotationManagerTest.java │ ├── annotationscan-classfile/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── annotationscan/ │ │ │ │ └── classfile/ │ │ │ │ ├── ClassfileAnnotationScanExtension.java │ │ │ │ ├── ClassfileAnnotationScanInitializer.java │ │ │ │ └── internal/ │ │ │ │ ├── InternalAnnotationScanAnnotationInfo.java │ │ │ │ └── InternalAnnotationScanAnnotationManager.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── extension/ │ │ └── annotationscan/ │ │ └── classfile/ │ │ ├── AnnotationScanExtensionTest.java │ │ ├── TestAnnotation.java │ │ ├── TestServlet.java │ │ ├── TestWithHandlesTypesInitializer.java │ │ └── internal/ │ │ └── InternalAnnotationScanAnnotationManagerTest.java │ ├── bytesstreamhandler/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── bytesstreamhandler/ │ │ │ │ ├── BytesStreamHandlerExtension.java │ │ │ │ ├── BytesStreamHandlerServletContextListener.java │ │ │ │ └── BytesStreamHandlerServletRequestListener.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── extension/ │ │ └── bytesstreamhandler/ │ │ └── BytesStreamHandlerExtensionTest.java │ ├── concurro/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── concurro/ │ │ │ └── ConcurroExtension.java │ │ └── module-info.java │ ├── coreprofile/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── coreprofile/ │ │ │ └── CoreProfileExtension.java │ │ └── module-info.java │ ├── datasource/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── datasource/ │ │ │ │ ├── DataSourceWrapper.java │ │ │ │ ├── DefaultDatasourceExtension.java │ │ │ │ ├── DefaultDatasourceInitializer.java │ │ │ │ ├── TxJoiningDataSource.java │ │ │ │ └── XADataSourceWrapper.java │ │ │ └── module-info.java │ │ └── site/ │ │ ├── markdown/ │ │ │ └── index.md │ │ └── site.xml │ ├── declared/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── declared/ │ │ │ │ ├── DeclaredExtension.java │ │ │ │ └── internal/ │ │ │ │ └── InternalDeclaredInitializer.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── extension/ │ │ ├── declared/ │ │ │ └── DeclaredExtensionTest.java │ │ └── handlestypes/ │ │ └── internal/ │ │ └── InternalDeclaredInitializerTest.java │ ├── eclipselink/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── eclipselink/ │ │ │ │ ├── EclipseLinkCdiExtension.java │ │ │ │ ├── EclipseLinkExtension.java │ │ │ │ ├── EclipseLinkInitializer.java │ │ │ │ ├── EntityManagerFactoryCreator.java │ │ │ │ ├── EntityManagerProducer.java │ │ │ │ ├── NonTxEntityManagerHolder.java │ │ │ │ ├── PiranhaEntityManager.java │ │ │ │ ├── TxEntityManagerHolder.java │ │ │ │ └── wrappers/ │ │ │ │ └── EntityManagerWrapper.java │ │ │ └── module-info.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── jakarta.enterprise.inject.spi.Extension │ ├── epicyro/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── epicyro/ │ │ │ │ ├── AuthenticationFilter.java │ │ │ │ ├── AuthenticationInitializer.java │ │ │ │ └── DoNothingServerAuthModule.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── extension/ │ │ └── epicyro/ │ │ └── AuthenticationInitializerTest.java │ ├── exousia/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── exousia/ │ │ │ ├── AuthorizationFilter.java │ │ │ ├── AuthorizationInitializer.java │ │ │ ├── AuthorizationPostInitializer.java │ │ │ ├── AuthorizationPreFilter.java │ │ │ ├── AuthorizationPreInitializer.java │ │ │ ├── PiranhaPrincipalMapper.java │ │ │ └── PiranhaToExousiaConverter.java │ │ └── module-info.java │ ├── expressly/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── expressly/ │ │ │ └── ExpresslyExtension.java │ │ └── module-info.java │ ├── fileupload/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── fileupload/ │ │ │ │ ├── FileUploadExtension.java │ │ │ │ ├── FileUploadMultiPart.java │ │ │ │ ├── FileUploadMultiPartInitializer.java │ │ │ │ └── FileUploadMultiPartManager.java │ │ │ └── module-info.java │ │ ├── site/ │ │ │ ├── markdown/ │ │ │ │ └── index.md │ │ │ └── site.xml │ │ └── test/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── fileupload/ │ │ │ └── tests/ │ │ │ └── FileUploadMultiPartManagerTest.java │ │ └── module-info.java │ ├── handlestypes/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── handlestypes/ │ │ │ │ ├── HandlesTypesExtension.java │ │ │ │ └── internal/ │ │ │ │ ├── InternalHandlesTypesInitializer.java │ │ │ │ └── InternalHandlesTypesManager.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── extension/ │ │ └── handlestypes/ │ │ ├── HandlesTypesExtensionTest.java │ │ └── internal/ │ │ ├── InternalHandlesTypesInitializerTest.java │ │ ├── InternalHandlesTypesManagerTest.java │ │ ├── TestA.java │ │ ├── TestB.java │ │ ├── TestC.java │ │ ├── TestD.java │ │ └── TestServletContainerInitializer.java │ ├── hazelcast/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ ├── cloud/ │ │ │ │ │ └── piranha/ │ │ │ │ │ └── extension/ │ │ │ │ │ └── hazelcast/ │ │ │ │ │ ├── HazelcastHttpSession.java │ │ │ │ │ ├── HazelcastHttpSessionManager.java │ │ │ │ │ └── HazelcastInitializer.java │ │ │ │ └── module-info.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── services/ │ │ │ └── jakarta.servlet.ServletContainerInitializer │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── extension/ │ │ └── hazelcast/ │ │ └── HazelcastInitializerTest.java │ ├── herring/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── herring/ │ │ │ │ ├── HerringExtension.java │ │ │ │ ├── HerringInitialContextFactory.java │ │ │ │ └── HerringServletRequestListener.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── extension/ │ │ └── herring/ │ │ └── HerringExtensionTest.java │ ├── hibernate-validator/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── hibernate/ │ │ │ └── validator/ │ │ │ └── HibernateValidatorExtension.java │ │ └── module-info.java │ ├── jaxb/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── jaxb/ │ │ │ └── JAXBExtension.java │ │ └── module-info.java │ ├── jersey/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── jersey/ │ │ │ │ ├── JerseyExtension.java │ │ │ │ ├── JerseySourceBean.java │ │ │ │ └── JerseyTargetBean.java │ │ │ └── module-info.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── jakarta.enterprise.inject.spi.Extension │ ├── jstl/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── jstl/ │ │ │ └── JSTLExtension.java │ │ └── module-info.java │ ├── micro/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── micro/ │ │ │ │ └── MicroExtension.java │ │ │ └── module-info.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── cloud.piranha.core.api.WebApplicationExtension │ ├── microprofile/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── microprofile/ │ │ │ └── MicroProfileExtension.java │ │ └── module-info.java │ ├── mojarra/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── mojarra/ │ │ │ └── MojarraExtension.java │ │ └── module-info.java │ ├── naming-cdi/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── naming/ │ │ │ │ └── cdi/ │ │ │ │ ├── DefaultInitialContextFactory.java │ │ │ │ ├── NamingExtension.java │ │ │ │ └── NamingServletRequestListener.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── extension/ │ │ └── naming/ │ │ └── NamingExtensionTest.java │ ├── omnifaces-config/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── omnifaces/ │ │ │ └── config/ │ │ │ └── ConfigExtension.java │ │ └── module-info.java │ ├── omnifaces-microprofile-jwt-auth/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── omnifaces/ │ │ │ └── microprofile/ │ │ │ └── jwt/ │ │ │ └── auth/ │ │ │ └── MicroProfileJWTAuthExtension.java │ │ └── module-info.java │ ├── omnifaces-omniservices/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── omniservices/ │ │ │ └── OmniServicesExtension.java │ │ └── module-info.java │ ├── omnifaces-omniutils/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── omniutils/ │ │ │ └── OmniUtilsExtension.java │ │ └── module-info.java │ ├── omnifish-omnibeans/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── omnibeans/ │ │ │ └── OmniBeansExtension.java │ │ └── module-info.java │ ├── omnifish-transact/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── transact/ │ │ │ ├── TransactExtension.java │ │ │ ├── TransactFilter.java │ │ │ └── TransactInitializer.java │ │ └── module-info.java │ ├── parsson/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── parsson/ │ │ │ └── ParssonExtension.java │ │ └── module-info.java │ ├── platform/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── platform/ │ │ │ └── PlatformExtension.java │ │ └── module-info.java │ ├── policy/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── policy/ │ │ │ │ ├── PolicyExtension.java │ │ │ │ └── internal/ │ │ │ │ ├── InternalPolicyServletContextListener.java │ │ │ │ ├── InternalPolicyServletRequestListener.java │ │ │ │ └── InternalPolicyThreadLocal.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── extension/ │ │ └── policy/ │ │ └── PolicyExtensionTest.java │ ├── pom.xml │ ├── scinitializer/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── scinitializer/ │ │ │ │ └── ServletContainerInitializerExtension.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── extension/ │ │ └── scinitializer/ │ │ └── ServletContainerInitializerExtensionTest.java │ ├── security-jakarta/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── security/ │ │ │ └── jakarta/ │ │ │ ├── JakartaSecurityAllInitializer.java │ │ │ └── JakartaSecurityExtension.java │ │ └── module-info.java │ ├── security-servlet/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── security/ │ │ │ │ └── servlet/ │ │ │ │ ├── ServletSecurityAllInitializer.java │ │ │ │ ├── ServletSecurityExtension.java │ │ │ │ ├── ServletSecurityManager.java │ │ │ │ ├── ServletSecurityManagerExtension.java │ │ │ │ ├── ServletSecurityManagerInitializer.java │ │ │ │ └── ServletSecurityPrincipal.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── extension/ │ │ └── security/ │ │ └── servlet/ │ │ ├── ServletSecurityManagerExtensionTest.java │ │ ├── ServletSecurityManagerInitializerTest.java │ │ └── ServletSecurityManagerTest.java │ ├── servlet/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── servlet/ │ │ │ └── ServletExtension.java │ │ └── module-info.java │ ├── servletannotations/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── servletannotations/ │ │ │ │ ├── ServletAnnotationsExtension.java │ │ │ │ └── ServletAnnotationsInitializer.java │ │ │ └── module-info.java │ │ └── test/ │ │ ├── java/ │ │ │ └── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── servletannotations/ │ │ │ ├── ServletAnnotationsExtensionTest.java │ │ │ ├── Test2Filter.java │ │ │ ├── Test2Servlet.java │ │ │ ├── Test3Filter.java │ │ │ ├── Test3Servlet.java │ │ │ ├── Test4Filter.java │ │ │ ├── Test4Servlet.java │ │ │ ├── Test5Servlet.java │ │ │ ├── TestFilter.java │ │ │ ├── TestListener.java │ │ │ ├── TestServlet.java │ │ │ └── WebServletTest.java │ │ └── webapp/ │ │ └── webservlet/ │ │ └── WEB-INF/ │ │ └── web.xml │ ├── soteria/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── soteria/ │ │ │ │ ├── IdentityStoreLoginHandler.java │ │ │ │ ├── SoteriaInitializer.java │ │ │ │ └── SoteriaPreCDIInitializer.java │ │ │ └── module-info.java │ │ └── test/ │ │ ├── java/ │ │ │ └── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── soteria/ │ │ │ ├── IdentityStoreLoginHandlerTest.java │ │ │ ├── SoteriaInitializerNoCdiTest.java │ │ │ ├── SoteriaInitializerTest.java │ │ │ ├── SoteriaPreCDIInitializerTest.java │ │ │ └── TestIdentityStore.java │ │ └── webapp/ │ │ └── WEB-INF/ │ │ └── beans.xml │ ├── tempdir/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── tempdir/ │ │ │ │ ├── TempDirExtension.java │ │ │ │ └── TempDirServletContainerInitializer.java │ │ │ └── module-info.java │ │ ├── site/ │ │ │ ├── markdown/ │ │ │ │ └── index.md │ │ │ └── site.xml │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── extension/ │ │ └── tempdir/ │ │ ├── TempDirExtensionTest.java │ │ └── TempDirServletContainerInitializerTest.java │ ├── tyrus/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── tyrus/ │ │ │ │ └── TyrusExtension.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── extension/ │ │ └── tyrus/ │ │ └── TyrusExtensionTest.java │ ├── wasp/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── wasp/ │ │ │ │ ├── WaspExtension.java │ │ │ │ ├── WaspInitializer.java │ │ │ │ ├── WaspJspManager.java │ │ │ │ ├── WaspJspManagerExtension.java │ │ │ │ ├── WaspJspManagerInitializer.java │ │ │ │ └── WaspServlet.java │ │ │ └── module-info.java │ │ ├── site/ │ │ │ └── site.xml │ │ └── test/ │ │ ├── java/ │ │ │ └── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── wasp/ │ │ │ ├── JspDescriptorTest.java │ │ │ ├── JspWriterTest.java │ │ │ ├── WaspExtensionTest.java │ │ │ ├── WaspInitializerTest.java │ │ │ └── WaspJspManagerExtensionTest.java │ │ └── webapp/ │ │ ├── jspwriter/ │ │ │ ├── clearBuffer.jsp │ │ │ ├── close.jsp │ │ │ ├── close2.jsp │ │ │ ├── close3.jsp │ │ │ └── close4.jsp │ │ └── waspinitializer/ │ │ └── WEB-INF/ │ │ ├── classes/ │ │ │ └── KEEPME │ │ └── lib/ │ │ └── KEEPME │ ├── webprofile/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── webprofile/ │ │ │ └── WebProfileExtension.java │ │ └── module-info.java │ ├── webxml/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── webxml/ │ │ │ │ ├── WebXmlExtension.java │ │ │ │ ├── WebXmlInitializer.java │ │ │ │ └── internal/ │ │ │ │ ├── WebXml.java │ │ │ │ ├── WebXmlContextParam.java │ │ │ │ ├── WebXmlDataSource.java │ │ │ │ ├── WebXmlErrorPage.java │ │ │ │ ├── WebXmlFilter.java │ │ │ │ ├── WebXmlFilterInitParam.java │ │ │ │ ├── WebXmlFilterMapping.java │ │ │ │ ├── WebXmlJspConfig.java │ │ │ │ ├── WebXmlJspConfigTaglib.java │ │ │ │ ├── WebXmlListener.java │ │ │ │ ├── WebXmlLoginConfig.java │ │ │ │ ├── WebXmlManager.java │ │ │ │ ├── WebXmlMimeMapping.java │ │ │ │ ├── WebXmlParser.java │ │ │ │ ├── WebXmlProcessor.java │ │ │ │ ├── WebXmlSecurityConstraint.java │ │ │ │ ├── WebXmlServlet.java │ │ │ │ ├── WebXmlServletInitParam.java │ │ │ │ ├── WebXmlServletMapping.java │ │ │ │ ├── WebXmlServletMultipartConfig.java │ │ │ │ ├── WebXmlServletSecurityRoleRef.java │ │ │ │ ├── WebXmlSessionConfig.java │ │ │ │ └── WebXmlSessionConfigCookieConfig.java │ │ │ └── module-info.java │ │ └── test/ │ │ ├── java/ │ │ │ └── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── webxml/ │ │ │ ├── TestFilter.java │ │ │ ├── TestServlet.java │ │ │ ├── WebXmlExtensionTest.java │ │ │ ├── WebXmlInitializerTest.java │ │ │ └── internal/ │ │ │ ├── ContextParamTest.java │ │ │ ├── DataSourceTest.java │ │ │ ├── DefaultContextPathTest.java │ │ │ ├── DenyUncoveredMethodsTest.java │ │ │ ├── EffectiveMajorVersionTest.java │ │ │ ├── ErrorPageTest.java │ │ │ ├── FilterMappingTest.java │ │ │ ├── JspConfigTest.java │ │ │ ├── MetadataCompleteTest.java │ │ │ ├── MimeMappingTest.java │ │ │ ├── TestWithoutLeadingSlashServlet.java │ │ │ ├── WebXmlManagerTest.java │ │ │ ├── WebXmlParserTest.java │ │ │ ├── WebXmlProcessorTest.java │ │ │ ├── WelcomeFileTest.java │ │ │ └── WithoutLeadingSlashTest.java │ │ └── webxml/ │ │ ├── contextParam/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ ├── dataSource/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ ├── defaultContextPath/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ ├── denyUncoveredMethods/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ ├── effectiveMajorVersion1/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ ├── errorPage/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ ├── filterMappings/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ ├── init/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ ├── init2/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ ├── init3/ │ │ │ └── WEB-INF/ │ │ │ └── classes/ │ │ │ └── META-INF/ │ │ │ └── web-fragment.xml │ │ ├── jspConfig/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ ├── metadataComplete/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ ├── mimeMapping/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ ├── parse/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ ├── parse2/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ ├── parse3/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ ├── parse4/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ ├── welcomeFile/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ └── withoutLeadingSlash1/ │ │ └── WEB-INF/ │ │ └── web.xml │ ├── welcomefile/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── welcomefile/ │ │ │ │ ├── WelcomeFileExtension.java │ │ │ │ └── internal/ │ │ │ │ └── InternalWelcomeFileManager.java │ │ │ └── module-info.java │ │ └── test/ │ │ ├── java/ │ │ │ └── cloud/ │ │ │ └── piranha/ │ │ │ └── extension/ │ │ │ └── welcomefile/ │ │ │ ├── WelcomeFileExtensionTest.java │ │ │ └── internal/ │ │ │ └── InternalWelcomeFileManagerTest.java │ │ └── webapp/ │ │ ├── welcomefile1/ │ │ │ └── index.html │ │ └── welcomefile2/ │ │ └── custom.html │ ├── weld/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── extension/ │ │ │ │ └── weld/ │ │ │ │ ├── RealtimeHttpServletRequestWrapper.java │ │ │ │ ├── WeldCDI.java │ │ │ │ ├── WeldContainer.java │ │ │ │ ├── WeldExtension.java │ │ │ │ ├── WeldHttpServletRequest.java │ │ │ │ ├── WeldInitListener.java │ │ │ │ ├── WeldInitializer.java │ │ │ │ ├── WeldObjectInstanceManager.java │ │ │ │ ├── WeldProvider.java │ │ │ │ └── WeldSecurityService.java │ │ │ └── module-info.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ ├── org.jboss.weld.bootstrap.api.Service │ │ └── org.jboss.weld.environment.servlet.Container │ └── yasson/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ ├── cloud/ │ │ └── piranha/ │ │ └── extension/ │ │ └── yasson/ │ │ └── YassonExtension.java │ └── module-info.java ├── feature/ │ ├── api/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── feature/ │ │ │ └── api/ │ │ │ ├── Feature.java │ │ │ └── FeatureManager.java │ │ └── module-info.java │ ├── crac/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── feature/ │ │ │ │ └── crac/ │ │ │ │ └── CracFeature.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── feature/ │ │ └── crac/ │ │ └── CracFeatureTest.java │ ├── exitonstop/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── feature/ │ │ │ │ └── exitonstop/ │ │ │ │ └── ExitOnStopFeature.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── feature/ │ │ └── exitonstop/ │ │ └── ExitOnStopFeatureTest.java │ ├── http/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── feature/ │ │ │ │ └── http/ │ │ │ │ └── HttpFeature.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── feature/ │ │ └── http/ │ │ └── HttpFeatureTest.java │ ├── https/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── feature/ │ │ │ │ └── https/ │ │ │ │ └── HttpsFeature.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── feature/ │ │ └── https/ │ │ └── HttpsFeatureTest.java │ ├── impl/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── feature/ │ │ │ │ └── impl/ │ │ │ │ ├── DefaultFeature.java │ │ │ │ └── DefaultFeatureManager.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── feature/ │ │ └── impl/ │ │ ├── DefaultFeatureManagerTest.java │ │ └── DefaultFeatureTest.java │ ├── isolatedwebapp/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── feature/ │ │ │ └── isolatedwebapp/ │ │ │ └── IsolatedWebAppFeature.java │ │ └── module-info.java │ ├── logging/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── feature/ │ │ │ │ └── logging/ │ │ │ │ ├── LoggingFeature.java │ │ │ │ └── LoggingHandler.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── feature/ │ │ └── logging/ │ │ └── LoggingFeatureTest.java │ ├── pid/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── feature/ │ │ │ └── pid/ │ │ │ └── PidFeature.java │ │ └── module-info.java │ ├── pom.xml │ ├── trace/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── feature/ │ │ │ │ └── trace/ │ │ │ │ └── TraceFeature.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── feature/ │ │ │ └── trace/ │ │ │ └── tests/ │ │ │ └── TraceFeatureTest.java │ │ └── module-info.java │ ├── webapp/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── feature/ │ │ │ │ └── webapp/ │ │ │ │ └── WebAppFeature.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── feature/ │ │ └── webapp/ │ │ └── WebAppFeatureTest.java │ └── webapps/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ ├── cloud/ │ │ └── piranha/ │ │ └── feature/ │ │ └── webapps/ │ │ └── WebAppsFeature.java │ └── module-info.java ├── http/ │ ├── api/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── http/ │ │ │ └── api/ │ │ │ ├── HttpServer.java │ │ │ ├── HttpServerProcessor.java │ │ │ ├── HttpServerProcessorEndState.java │ │ │ ├── HttpServerRequest.java │ │ │ ├── HttpServerResponse.java │ │ │ └── package-info.java │ │ └── module-info.java │ ├── crac/ │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── http/ │ │ │ └── crac/ │ │ │ └── CracHttpServer.java │ │ └── module-info.java │ ├── grizzly/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── http/ │ │ │ │ └── grizzly/ │ │ │ │ ├── GrizzlyHttpServer.java │ │ │ │ ├── GrizzlyHttpServerRequest.java │ │ │ │ ├── GrizzlyHttpServerResponse.java │ │ │ │ └── package-info.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── http/ │ │ └── grizzly/ │ │ ├── GrizzlyHttpServerRequestTest.java │ │ └── GrizzlyHttpServerTest.java │ ├── impl/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── http/ │ │ │ │ └── impl/ │ │ │ │ ├── DefaultHttpServer.java │ │ │ │ ├── DefaultHttpServerAcceptorThread.java │ │ │ │ ├── DefaultHttpServerProcessingThread.java │ │ │ │ ├── DefaultHttpServerProcessor.java │ │ │ │ ├── DefaultHttpServerRequest.java │ │ │ │ ├── DefaultHttpServerResponse.java │ │ │ │ ├── DefaultHttpServerThreadFactory.java │ │ │ │ └── package-info.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── http/ │ │ └── impl/ │ │ ├── DefaultHttpServerRequestTest.java │ │ └── DefaultHttpServerTest.java │ ├── jdk/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── http/ │ │ │ │ └── jdk/ │ │ │ │ ├── JdkHttpHandler.java │ │ │ │ ├── JdkHttpRequest.java │ │ │ │ ├── JdkHttpResponse.java │ │ │ │ ├── JdkHttpServer.java │ │ │ │ └── package-info.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── http/ │ │ └── jdk/ │ │ └── JdkHttpServerTest.java │ ├── netty/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── http/ │ │ │ │ └── netty/ │ │ │ │ ├── NettyHttpServer.java │ │ │ │ ├── NettyHttpServerHandler.java │ │ │ │ ├── NettyHttpServerInitializer.java │ │ │ │ ├── NettyHttpServerRequest.java │ │ │ │ ├── NettyHttpServerResponse.java │ │ │ │ └── package-info.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── http/ │ │ └── netty/ │ │ └── NettyHttpServerTest.java │ ├── pom.xml │ ├── tests/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── http/ │ │ │ └── tests/ │ │ │ ├── HttpServerRequestTest.java │ │ │ ├── HttpServerTest.java │ │ │ └── TestHttpServerProcessor.java │ │ └── module-info.java │ ├── undertow/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── http/ │ │ │ │ └── undertow/ │ │ │ │ ├── UndertowHttpHandler.java │ │ │ │ ├── UndertowHttpRequest.java │ │ │ │ ├── UndertowHttpResponse.java │ │ │ │ ├── UndertowHttpServer.java │ │ │ │ └── package-info.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── http/ │ │ └── undertow/ │ │ └── UndertowHttpServerTest.java │ ├── virtual/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── http/ │ │ │ │ └── virtual/ │ │ │ │ └── VirtualHttpServer.java │ │ │ └── module-info.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── http/ │ │ └── virtual/ │ │ └── VirtualHttpServerTest.java │ └── webapp/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── http/ │ │ │ └── webapp/ │ │ │ ├── HttpWebApplicationOutputStream.java │ │ │ ├── HttpWebApplicationRequest.java │ │ │ ├── HttpWebApplicationResponse.java │ │ │ ├── HttpWebApplicationServer.java │ │ │ └── HttpWebApplicationServerRequestMapper.java │ │ └── module-info.java │ └── test/ │ └── java/ │ └── cloud/ │ └── piranha/ │ └── http/ │ └── webapp/ │ ├── HttpWebApplicationRequestTest.java │ ├── HttpWebApplicationResponseTest.java │ ├── HttpWebApplicationServerTest.java │ └── TestSnoopServlet.java ├── maven/ │ ├── archetypes/ │ │ └── pom.xml │ ├── plugin/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── maven/ │ │ │ │ └── plugin/ │ │ │ │ ├── BaseMojo.java │ │ │ │ ├── RunMojo.java │ │ │ │ ├── StartMojo.java │ │ │ │ └── StopMojo.java │ │ │ └── module-info.java │ │ └── site/ │ │ └── site.xml │ └── pom.xml ├── micro/ │ ├── builder/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── micro/ │ │ │ └── builder/ │ │ │ ├── MicroEmbeddedPiranha.java │ │ │ ├── MicroEmbeddedPiranhaBuilder.java │ │ │ ├── MicroWebApplication.java │ │ │ └── package-info.java │ │ └── module-info.java │ ├── core/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── micro/ │ │ │ │ └── core/ │ │ │ │ ├── CdiExtension.java │ │ │ │ ├── DefaultAnnotationInfo.java │ │ │ │ ├── InMemoryIdentityStore.java │ │ │ │ ├── MicroInnerApplication.java │ │ │ │ ├── MicroInnerDeployer.java │ │ │ │ └── PiranhaBeanArchiveHandler.java │ │ │ └── module-info.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ ├── jakarta.enterprise.inject.spi.Extension │ │ └── org.jboss.weld.environment.deployment.discovery.BeanArchiveHandler │ ├── loader/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── micro/ │ │ │ └── loader/ │ │ │ ├── MicroConfiguration.java │ │ │ ├── MicroDeployOutcome.java │ │ │ ├── MicroInfo.java │ │ │ ├── MicroOuterDeployer.java │ │ │ ├── StaticStreamHandler.java │ │ │ ├── StaticURLStreamHandlerFactory.java │ │ │ └── package-info.java │ │ └── module-info.java │ └── pom.xml ├── multi/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── assembly/ │ │ │ └── zip.xml │ │ ├── docker/ │ │ │ └── Dockerfile │ │ ├── java/ │ │ │ ├── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── multi/ │ │ │ │ ├── MultiPiranha.java │ │ │ │ ├── MultiPiranhaBuilder.java │ │ │ │ ├── MultiPiranhaMain.java │ │ │ │ └── MultiWebApplication.java │ │ │ └── module-info.java │ │ └── zip/ │ │ ├── etc/ │ │ │ ├── keystore.jks │ │ │ ├── logging.properties │ │ │ └── truststore.jks │ │ ├── tmp/ │ │ │ └── README │ │ └── webapps/ │ │ └── README │ └── test/ │ └── java/ │ └── cloud/ │ └── piranha/ │ └── multi/ │ ├── MultiPiranhaBuilderTest.java │ └── MultiPiranhaIT.java ├── pom.xml ├── resource/ │ ├── api/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── resource/ │ │ │ └── api/ │ │ │ ├── Resource.java │ │ │ ├── ResourceManager.java │ │ │ ├── ResourceManagerClassLoader.java │ │ │ └── package-info.java │ │ └── module-info.java │ ├── impl/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ ├── cloud/ │ │ │ │ │ └── piranha/ │ │ │ │ │ └── resource/ │ │ │ │ │ └── impl/ │ │ │ │ │ ├── AliasedDirectoryResource.java │ │ │ │ │ ├── AliasedNamedResource.java │ │ │ │ │ ├── ByteArrayResource.java │ │ │ │ │ ├── ByteArrayResourceStreamHandlerProvider.java │ │ │ │ │ ├── ByteArrayResourceURLConnection.java │ │ │ │ │ ├── ByteArrayResourceURLStreamHandler.java │ │ │ │ │ ├── ClassResource.java │ │ │ │ │ ├── DefaultResourceManager.java │ │ │ │ │ ├── DefaultResourceManagerClassLoader.java │ │ │ │ │ ├── DirectoryResource.java │ │ │ │ │ ├── JarResource.java │ │ │ │ │ ├── MultiReleaseResource.java │ │ │ │ │ ├── PrefixJarResource.java │ │ │ │ │ └── StringResource.java │ │ │ │ └── module-info.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── services/ │ │ │ └── java.net.spi.URLStreamHandlerProvider │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── resource/ │ │ └── impl/ │ │ ├── AliasedDirectoryResourceTest.java │ │ ├── AliasedNamedResourceTest.java │ │ ├── ByteArrayResourceTest.java │ │ ├── DefaultResourceManagerTest.java │ │ ├── DirectoryResourceTest.java │ │ ├── JarResourceTest.java │ │ ├── MultiReleaseResourceTest.java │ │ └── PrefixJarResourceTest.java │ ├── pom.xml │ └── shrinkwrap/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── resource/ │ │ │ └── shrinkwrap/ │ │ │ ├── ArchiveURLStreamHandler.java │ │ │ ├── GlobalArchiveStreamHandler.java │ │ │ ├── IsolatingResourceManagerClassLoader.java │ │ │ ├── NodeURLStreamHandler.java │ │ │ ├── ShrinkWrapDirectoryInputStream.java │ │ │ ├── ShrinkWrapResource.java │ │ │ └── StreamConnection.java │ │ └── module-info.java │ └── test/ │ └── java/ │ └── cloud/ │ └── piranha/ │ └── resource/ │ └── shrinkwrap/ │ ├── DefaultResourceManagerTest.java │ └── DefaultWebApplicationClassLoaderTest.java ├── single/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ ├── cloud/ │ │ │ └── piranha/ │ │ │ └── single/ │ │ │ ├── SingleMain.java │ │ │ ├── SinglePiranha.java │ │ │ └── SinglePiranhaBuilder.java │ │ └── module-info.java │ └── test/ │ └── java/ │ └── cloud/ │ └── piranha/ │ └── single/ │ ├── SinglePiranhaBuilderTest.java │ └── SinglePiranhaTest.java ├── spring/ │ ├── pom.xml │ └── spring-boot-starter-piranha-embedded/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ ├── cloud/ │ │ └── piranha/ │ │ └── spring/ │ │ └── starter/ │ │ └── embedded/ │ │ ├── EmbeddedPiranhaServletWebServerFactory.java │ │ └── EmbeddedPiranhaWebServer.java │ └── module-info.java ├── src/ │ └── site/ │ ├── markdown/ │ │ └── index.md │ └── site.xml └── test/ ├── common/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ ├── cloud/ │ │ └── piranha/ │ │ └── test/ │ │ └── common/ │ │ └── PiranhaStartup.java │ └── module-info.java ├── coreprofile/ │ ├── arquillian/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── arquillian/ │ │ │ ├── HelloArquillianApplication.java │ │ │ └── HelloArquillianBean.java │ │ └── test/ │ │ └── java/ │ │ └── helloarquillian/ │ │ └── HelloArquillianIT.java │ ├── integration/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── test/ │ │ │ │ └── coreprofile/ │ │ │ │ └── distribution/ │ │ │ │ ├── AsyncBean.java │ │ │ │ ├── BeanParamBean.java │ │ │ │ ├── BeanParamInput.java │ │ │ │ ├── ContainerRequestContextBean.java │ │ │ │ ├── ContainerRequestContextFilter.java │ │ │ │ ├── DependencyInjectionBean.java │ │ │ │ ├── IntegrationApplication.java │ │ │ │ ├── InterceptInterceptor.java │ │ │ │ ├── InterceptedBean.java │ │ │ │ ├── Jsonb.java │ │ │ │ ├── ProducesBean.java │ │ │ │ ├── RestBean.java │ │ │ │ ├── SseBean.java │ │ │ │ ├── SseBroadcastBean.java │ │ │ │ └── TRACE.java │ │ │ └── webapp/ │ │ │ └── WEB-INF/ │ │ │ └── beans.xml │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── test/ │ │ └── coreprofile/ │ │ └── distribution/ │ │ ├── AsyncIT.java │ │ ├── BeanParamIT.java │ │ ├── ContainerRequestContextIT.java │ │ ├── DependencyInjectionIT.java │ │ ├── ITBase.java │ │ ├── InterceptorIT.java │ │ ├── JsonBindingIT.java │ │ ├── JsonProcessingIT.java │ │ ├── ProducesIT.java │ │ ├── RestIT.java │ │ └── SseIT.java │ ├── json/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── temperature/ │ │ │ ├── Temperature.java │ │ │ ├── TemperatureApplication.java │ │ │ └── TemperatureBean.java │ │ └── test/ │ │ └── java/ │ │ └── temperature/ │ │ └── TemperatureIT.java │ ├── no_servlet_class/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── test/ │ │ │ │ └── coreprofile/ │ │ │ │ └── no_servlet_class/ │ │ │ │ ├── EchoApplication.java │ │ │ │ └── EchoBean.java │ │ │ └── webapp/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── test/ │ │ └── coreprofile/ │ │ └── no_servlet_class/ │ │ └── EchoIT.java │ ├── pom.xml │ └── rest/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── cloud/ │ │ │ └── piranha/ │ │ │ └── test/ │ │ │ └── coreprofile/ │ │ │ └── rest/ │ │ │ ├── RestApplication.java │ │ │ └── RestBean.java │ │ └── webapp/ │ │ └── WEB-INF/ │ │ └── web.xml │ └── test/ │ └── java/ │ └── cloud/ │ └── piranha/ │ └── test/ │ └── coreprofile/ │ └── rest/ │ └── RestIT.java ├── embedded/ │ ├── classloader/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── classloader/ │ │ │ └── ClassLoaderServlet.java │ │ └── webapp/ │ │ └── META-INF/ │ │ └── context.xml │ ├── classloader2/ │ │ ├── pom.xml │ │ └── src/ │ │ └── test/ │ │ └── java/ │ │ └── classloader/ │ │ └── ClassLoaderIT.java │ ├── eclipselink/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── eclipselink/ │ │ │ │ ├── EclipseLinkBean.java │ │ │ │ └── EclipseLinkTable.java │ │ │ ├── resources/ │ │ │ │ └── META-INF/ │ │ │ │ └── persistence.xml │ │ │ └── webapp/ │ │ │ ├── WEB-INF/ │ │ │ │ ├── beans.xml │ │ │ │ ├── faces-config.xml │ │ │ │ └── web.xml │ │ │ └── index.xhtml │ │ └── test/ │ │ └── java/ │ │ └── eclipselink/ │ │ └── EclipseLinkTest.java │ ├── exousia/ │ │ ├── pom.xml │ │ └── src/ │ │ └── test/ │ │ └── java/ │ │ └── exousia/ │ │ ├── ExousiaTest.java │ │ └── PublicServlet.java │ ├── hazelcast/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── hazelcast/ │ │ │ │ └── HazelcastBean.java │ │ │ └── webapp/ │ │ │ ├── WEB-INF/ │ │ │ │ ├── beans.xml │ │ │ │ └── web.xml │ │ │ └── index.xhtml │ │ └── test/ │ │ └── java/ │ │ └── hazelcast/ │ │ └── HazelcastTest.java │ ├── helloworld/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── helloworld/ │ │ │ │ ├── HelloWorldApplication.java │ │ │ │ └── HelloWorldServlet.java │ │ │ └── webapp/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ └── test/ │ │ └── java/ │ │ └── helloworld/ │ │ └── HelloWorldTest.java │ ├── pom.xml │ ├── soteria-basic/ │ │ ├── pom.xml │ │ └── src/ │ │ └── test/ │ │ ├── java/ │ │ │ └── basic/ │ │ │ ├── BasicTest.java │ │ │ ├── DynamicInitialContextFactory.java │ │ │ ├── ProtectedServlet.java │ │ │ ├── PublicServlet.java │ │ │ └── TestIdentityStore.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── beans.xml │ ├── soteria-form/ │ │ ├── pom.xml │ │ └── src/ │ │ └── test/ │ │ ├── java/ │ │ │ └── form/ │ │ │ ├── DynamicInitialContextFactory.java │ │ │ ├── ErrorPageServlet.java │ │ │ ├── FormTest.java │ │ │ ├── LoginPageServlet.java │ │ │ ├── ProtectedServlet.java │ │ │ └── TestIdentityStore.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── beans.xml │ ├── springboot/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── test1/ │ │ │ ├── Test1Application.java │ │ │ └── Test1Controller.java │ │ └── resources/ │ │ └── application.properties │ ├── springboot-virtualthreads/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── test/ │ │ └── custom/ │ │ └── http/ │ │ ├── Main.java │ │ └── MyController.java │ ├── web-fragment-in-jar/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── resources/ │ │ └── META-INF/ │ │ └── web-fragment.xml │ ├── web-fragment-in-jar-test/ │ │ ├── pom.xml │ │ └── src/ │ │ └── test/ │ │ └── java/ │ │ └── webfragmentinjar/ │ │ └── WebFragmentInJar1IT.java │ ├── web-fragment-in-jar-webapp/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── webapp/ │ │ ├── META-INF/ │ │ │ └── context.xml │ │ └── index.html │ └── weld/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── weld/ │ │ │ └── WeldRequestBean.java │ │ └── webapp/ │ │ ├── WEB-INF/ │ │ │ ├── beans.xml │ │ │ ├── faces-config.xml │ │ │ └── web.xml │ │ └── index.xhtml │ └── test/ │ └── java/ │ └── weld/ │ └── WeldTest.java ├── jpms/ │ ├── pom.xml │ └── src/ │ └── test/ │ └── java/ │ ├── jpms/ │ │ └── ModuleFinderTest.java │ ├── package1/ │ │ ├── Library.java │ │ ├── Service.java │ │ ├── Servlet.java │ │ └── Utils.java │ ├── package2/ │ │ ├── Library.java │ │ └── Listener.java │ └── package3/ │ ├── DefaultService.java │ ├── Filter.java │ └── Library.java ├── micro/ │ ├── helloworld/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── helloworld/ │ │ │ │ └── HelloWorldServlet.java │ │ │ └── webapp/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ └── test/ │ │ └── java/ │ │ └── helloworld/ │ │ └── HelloWorldServletIT.java │ ├── microprofile-config/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── config/ │ │ │ │ ├── ConfigApplicationBean.java │ │ │ │ └── ConfigServlet.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── microprofile-config.properties │ │ └── test/ │ │ └── java/ │ │ └── config/ │ │ └── ConfigTest.java │ ├── pom.xml │ ├── protected-servlet/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── protectedservlet/ │ │ │ │ ├── ProtectedBean.java │ │ │ │ └── ProtectedServlet.java │ │ │ └── webapp/ │ │ │ └── WEB-INF/ │ │ │ ├── beans.xml │ │ │ └── web.xml │ │ └── test/ │ │ └── java/ │ │ └── protectedservlet/ │ │ └── ProtectedServletIT.java │ └── snoop/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── snoop/ │ │ │ └── SnoopServlet.java │ │ └── webapp/ │ │ └── WEB-INF/ │ │ ├── beans.xml │ │ └── web.xml │ └── test/ │ └── java/ │ └── snoop/ │ └── SnoopIT.java ├── pom.xml ├── server/ │ ├── helloworld/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── webapp/ │ │ │ └── helloworld.html │ │ └── test/ │ │ └── java/ │ │ └── helloworld/ │ │ └── HelloWorldIT.java │ ├── pom.xml │ ├── programmatic/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── programmatic/ │ │ │ │ └── ProgrammaticServlet.java │ │ │ └── webapp/ │ │ │ └── WEB-INF/ │ │ │ └── web.xml │ │ └── test/ │ │ └── java/ │ │ └── programmatic/ │ │ └── ProgrammaticIT.java │ ├── wasp/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── wasp/ │ │ │ │ └── HelloTldTag.java │ │ │ └── webapp/ │ │ │ ├── WEB-INF/ │ │ │ │ ├── tags/ │ │ │ │ │ └── hellotag.tag │ │ │ │ ├── tld/ │ │ │ │ │ ├── hellotag.tld │ │ │ │ │ └── hellotld.tld │ │ │ │ └── web.xml │ │ │ ├── hellotag.jsp │ │ │ ├── hellotag2.jsp │ │ │ ├── hellotld.jsp │ │ │ ├── hellotld2.jsp │ │ │ └── index.jsp │ │ └── test/ │ │ └── java/ │ │ └── wasp/ │ │ ├── HelloTagJspIT.java │ │ ├── HelloTld2JspIT.java │ │ ├── HelloTldJspIT.java │ │ └── IndexJspIT.java │ └── wasp2/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── resources/ │ └── META-INF/ │ ├── hellotag2.tag │ └── hellotag2.tld ├── servlet/ │ ├── crac/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── test/ │ │ │ │ └── servlet/ │ │ │ │ └── crac/ │ │ │ │ └── CracBean.java │ │ │ └── webapp/ │ │ │ ├── WEB-INF/ │ │ │ │ ├── beans.xml │ │ │ │ └── web.xml │ │ │ └── crac.xhtml │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── test/ │ │ └── servlet/ │ │ └── crac/ │ │ └── CracIT.java │ ├── hello/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── hello/ │ │ │ │ └── HelloServlet.java │ │ │ └── webapp/ │ │ │ ├── WEB-INF/ │ │ │ │ └── web.xml │ │ │ ├── helloel.jsp │ │ │ ├── hellojsp.jsp │ │ │ └── helloworld.html │ │ └── test/ │ │ └── java/ │ │ └── hello/ │ │ └── HelloIT.java │ ├── helloworld/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── webapp/ │ │ │ └── helloworld.html │ │ └── test/ │ │ └── java/ │ │ └── helloworld/ │ │ └── HelloWorldIT.java │ ├── integration/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── cloud/ │ │ │ └── piranha/ │ │ │ └── test/ │ │ │ └── servlet/ │ │ │ └── integration/ │ │ │ └── IntegrationServlet.java │ │ └── test/ │ │ └── java/ │ │ └── cloud/ │ │ └── piranha/ │ │ └── test/ │ │ └── servlet/ │ │ └── integration/ │ │ └── IntegrationtIT.java │ ├── pages/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── webapp/ │ │ │ └── hello.jsp │ │ └── test/ │ │ └── java/ │ │ └── hello/ │ │ └── HelloIT.java │ ├── pom.xml │ └── websocket/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── chat/ │ └── ChatEndpoint.java ├── tck/ │ ├── coreprofile/ │ │ ├── annotations/ │ │ │ ├── installer/ │ │ │ │ └── pom.xml │ │ │ ├── pom.xml │ │ │ └── runner/ │ │ │ └── pom.xml │ │ ├── cdi/ │ │ │ ├── installer/ │ │ │ │ └── pom.xml │ │ │ ├── pom.xml │ │ │ └── runner/ │ │ │ ├── core/ │ │ │ │ ├── pom.xml │ │ │ │ └── src/ │ │ │ │ └── test/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── jboss/ │ │ │ │ │ └── weld/ │ │ │ │ │ └── tck/ │ │ │ │ │ └── piranha/ │ │ │ │ │ ├── PiranhaDeploymentExceptionTransformer.java │ │ │ │ │ ├── PiranhaExtension.java │ │ │ │ │ ├── WeldBeansImpl.java │ │ │ │ │ ├── WeldContextImpl.java │ │ │ │ │ └── WeldELImpl.java │ │ │ │ └── resources/ │ │ │ │ ├── META-INF/ │ │ │ │ │ ├── cdi-tck.properties │ │ │ │ │ └── services/ │ │ │ │ │ └── org.jboss.arquillian.core.spi.LoadableExtension │ │ │ │ └── arquillian.xml │ │ │ ├── model/ │ │ │ │ ├── pom.xml │ │ │ │ └── src/ │ │ │ │ └── test/ │ │ │ │ └── java/ │ │ │ │ └── org/ │ │ │ │ └── glassfish/ │ │ │ │ └── tck/ │ │ │ │ └── cdi/ │ │ │ │ └── lang/ │ │ │ │ └── model/ │ │ │ │ ├── CDILangModelTCKRunner.java │ │ │ │ └── LangModelVerifierBuildCompatibleExtension.java │ │ │ ├── pom.xml │ │ │ └── signature/ │ │ │ └── pom.xml │ │ ├── coreprofile/ │ │ │ ├── installer/ │ │ │ │ └── pom.xml │ │ │ ├── pom.xml │ │ │ └── runner/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── test/ │ │ │ ├── java/ │ │ │ │ └── cloud/ │ │ │ │ └── piranha/ │ │ │ │ └── tck/ │ │ │ │ └── core/ │ │ │ │ └── CustomJsonbSerializationITFix.java │ │ │ └── resources/ │ │ │ ├── META-INF/ │ │ │ │ └── services/ │ │ │ │ └── org.jboss.arquillian.core.spi.LoadableExtension │ │ │ └── arquillian.xml │ │ ├── inject/ │ │ │ ├── installer/ │ │ │ │ └── pom.xml │ │ │ ├── pom.xml │ │ │ └── runner/ │ │ │ └── pom.xml │ │ ├── jsonb/ │ │ │ ├── installer/ │ │ │ │ └── pom.xml │ │ │ ├── pom.xml │ │ │ └── runner/ │ │ │ └── pom.xml │ │ ├── jsonp/ │ │ │ ├── installer/ │ │ │ │ └── pom.xml │ │ │ ├── pom.xml │ │ │ └── runner/ │ │ │ └── pom.xml │ │ ├── pom.xml │ │ └── rest/ │ │ ├── installer/ │ │ │ └── pom.xml │ │ ├── pom.xml │ │ └── runner/ │ │ ├── pom.xml │ │ └── src/ │ │ └── test/ │ │ └── resources/ │ │ └── arquillian.xml │ └── pom.xml └── webprofile/ ├── helloworld/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── helloworld/ │ │ │ └── ApplicationBean.java │ │ └── webapp/ │ │ ├── WEB-INF/ │ │ │ └── beans.xml │ │ ├── helloworld.jsp │ │ └── index.html │ └── test/ │ └── java/ │ └── helloworld/ │ └── HelloWorldIT.java ├── integration/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── integration/ │ │ │ ├── DependencyInjectionBean.java │ │ │ ├── ExpressionBean.java │ │ │ ├── FacesBean.java │ │ │ ├── IntegrationApplication.java │ │ │ ├── IntegrationBean.java │ │ │ ├── IntegrationServlet.java │ │ │ ├── InterceptBean.java │ │ │ ├── InterceptInterceptor.java │ │ │ └── Jsonb.java │ │ └── webapp/ │ │ ├── WEB-INF/ │ │ │ ├── beans.xml │ │ │ └── web.xml │ │ ├── expression.jsp │ │ ├── faces.xhtml │ │ ├── jstl.jsp │ │ └── pages.jsp │ └── test/ │ └── java/ │ └── integration/ │ └── IntegrationIT.java └── pom.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ github: mnriem ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: # Enable version updates for Maven - package-ecosystem: "maven" directory: "/" schedule: interval: "weekly" open-pull-requests-limit: 10 # Enable version updates for GitHub Actions - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" open-pull-requests-limit: 10 ================================================ FILE: .github/workflows/current.yml ================================================ name: current on: push: branches: - 'current' tags-ignore: - 'v*' workflow_dispatch: jobs: build: if: github.repository == 'piranhacloud/piranha' runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: java: [ 21 ] os: [ubuntu-latest, windows-latest] steps: - name: Checkout Sources uses: actions/checkout@v6 - name: Setup Java ${{ matrix.java }} uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: ${{ matrix.java }} - name: Build with Maven run: mvn -B -ntp -T 1 install docs: if: github.repository == 'piranhacloud/piranha' runs-on: ubuntu-latest steps: - name: Checkout Piranha uses: actions/checkout@v6 - name: Checkout Piranha Website uses: actions/checkout@v6 with: repository: piranhacloud/piranha-website token: ${{ secrets.GIT_PASSWORD }} path: piranha-website ref: 'gh-pages' - name: Setup Java uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: 21 - name: Build with Maven run: | mvn -B -DskipTests=true -DskipITs=true -DstagingDirectory=$PWD/target/staging -ntp -P '!test' -T 1 clean install site site:stage rm -rf piranha-website/snapshot || true mkdir -p piranha-website/snapshot || true cp -R target/staging/* piranha-website/snapshot/ cd piranha-website git config --global user.email "noreply@piranha.cloud" git config --global user.name "Automated publish" git add . git commit -a -m "Publishing SNAPSHOT documentation" || true git push ghcr: if: github.repository == 'piranhacloud/piranha' runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v6 - name: Setup Java uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: 21 - name: Build with Maven run: mvn -B -Dmaven.javadoc.skip=true -DskipTests=true -DskipITs=true -ntp -P '!test' verify - name: Login to GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 with: version: v0.10.0 - run: docker buildx inspect - name: Build Docker images run: | mvn -B -Dmaven.javadoc.skip=true -DskipTests=true -DskipITs=true -ntp -P '!test' install cd docker mvn -B -DskipTests=true -DskipITs=true -ntp deploy - name: Build Piranha Isolated image run: | mvn -B -Dmaven.javadoc.skip=true -DskipTests=true -DskipITs=true -ntp -P '!test' install cd dist/isolated mvn -B -DskipTests=true -DskipITs=true -ntp -P docker deploy - name: Build Piranha Micro image run: | mvn -B -Dmaven.javadoc.skip=true -DskipTests=true -DskipITs=true -ntp -P '!test' install cd dist/micro mvn -B -DskipTests=true -DskipITs=true -ntp -P docker deploy - name: Build Piranha MicroProfile image run: | mvn -B -Dmaven.javadoc.skip=true -DskipTests=true -DskipITs=true -ntp -P '!test' install cd dist/microprofile mvn -B -DskipTests=true -DskipITs=true -ntp -P docker deploy - name: Build Piranha Platform image run: | mvn -B -Dmaven.javadoc.skip=true -DskipTests=true -DskipITs=true -ntp -P '!test' install cd dist/platform mvn -B -DskipTests=true -DskipITs=true -ntp -P docker deploy - name: Build Piranha Server image run: | mvn -B -Dmaven.javadoc.skip=true -DskipTests=true -DskipITs=true -ntp -P '!test' install cd dist/server mvn -B -DskipTests=true -DskipITs=true -ntp -P docker deploy - name: Build Piranha Servlet image run: | mvn -B -Dmaven.javadoc.skip=true -DskipTests=true -DskipITs=true -ntp -P '!test' install cd dist/servlet mvn -B -DskipTests=true -DskipITs=true -ntp -P docker deploy - name: Build Piranha Web Profile image run: | mvn -B -Dmaven.javadoc.skip=true -DskipTests=true -DskipITs=true -ntp -P '!test' install cd dist/webprofile mvn -B -DskipTests=true -DskipITs=true -ntp -P docker deploy sonatype: if: false # github.repository == 'piranhacloud/piranha' runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v6 - name: Setup Java uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: 21 server-id: ossrh server-username: MAVEN_USERNAME server-password: MAVEN_PASSWORD - name: Build with Maven run: mvn -B -DskipTests=true -P '!test' -ntp deploy env: MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} ================================================ FILE: .github/workflows/docs.yml ================================================ name: docs on: workflow_dispatch: inputs: version: description: 'Release version' required: true jobs: docs: runs-on: ubuntu-latest steps: - name: Checkout Piranha uses: actions/checkout@v6 with: ref: v${{ github.event.inputs.version }} - name: Checkout Piranha Website uses: actions/checkout@v6 with: repository: piranhacloud/piranha-website token: ${{ secrets.GIT_PASSWORD }} path: piranha-website ref: 'gh-pages' - name: Setup Java uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: 21 - name: Build with Maven run: | mvn -B -DskipTests -DskipITs -DstagingDirectory=$PWD/target/staging -ntp -P '!test' -T 1 install site site:stage rm -rf piranha-website/release || true mkdir -p piranha-website/release || true cp -R target/staging/* piranha-website/release/ cd piranha-website git config --global user.email "noreply@piranha.cloud" git config --global user.name "Automated publish" git add . git commit -a -m "Publishing ${{ github.event.inputs.version }} documentation" || true git push ================================================ FILE: .github/workflows/experimental.yml ================================================ name: experimental on: schedule: - cron: "0 0 * * 0" workflow_dispatch: jobs: build: if: github.repository == 'piranhacloud/piranha' runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: distribution: [ 'temurin' ] java: [ '23', '24-ea', '25-ea' ] os: [ubuntu-latest, macos-latest, windows-latest] steps: - name: Checkout Sources uses: actions/checkout@v6 - name: Setup Java ${{ matrix.java }} uses: actions/setup-java@v4 with: distribution: ${{ matrix.distribution }} java-version: ${{ matrix.java }} - name: Build with Maven run: mvn -B -ntp -T 1 install result: if: ${{ always() }} && github.repository == 'piranhacloud/piranha' runs-on: ubuntu-latest needs: build steps: - name: Check build results run: | result="${{ needs.build.result }}" if [[ $result == "success" || $result == "skipped" ]]; then echo "All matrix jobs were successful or skipped." exit 0 else echo "One or more matrix jobs failed." exit 0 fi ================================================ FILE: .github/workflows/pr.yml ================================================ name: pr on: pull_request: branches: - '*' jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: java: [ 21 ] os: [ubuntu-latest, macos-latest, windows-latest] steps: - name: Checkout Sources uses: actions/checkout@v6 - name: Setup Java ${{ matrix.java }} uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: ${{ matrix.java }} - name: Build with Maven run: mvn -B -ntp -T 1 install - name: Build Docker images if: matrix.os == 'ubuntu-latest' run: | cd docker mvn -B -ntp install codeql: name: codeql runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write steps: - name: Checkout sources uses: actions/checkout@v6 - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: languages: 'java' - name: Setup Java uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '21' - name: Build with Maven run: mvn -B -DskipTests=true -DskipITs=true -ntp install - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 docs: runs-on: ubuntu-latest steps: - name: Checkout Piranha uses: actions/checkout@v6 - name: Setup Java uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: 21 - name: Build with Maven run: | mvn -B -DskipTests=true -DskipITs=true -ntp -T 1 install site ================================================ FILE: .github/workflows/release.yml ================================================ name: release on: push: tags: - v* jobs: build: runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v6 - name: Setup Java uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: 21 server-id: ossrh server-username: MAVEN_USERNAME server-password: MAVEN_PASSWORD gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} gpg-passphrase: MAVEN_GPG_PASSPHRASE - name: Build with Maven run: mvn --no-transfer-progress -B -DskipTests=true install - name: Login to GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 with: version: v0.10.0 - run: docker buildx inspect # - run: docker buildx inspect # - name: Build Docker images # run: | # mvn -B -Dmaven.javadoc.skip=true -DskipTests=true -DskipITs=true -ntp -P '!test' install # cd docker # mvn -B -DskipTests=true -DskipITs=true -ntp -P docker docker:deploy - name: Build Piranha Micro image run: | mvn -B -Dmaven.javadoc.skip=true -DskipTests=true -DskipITs=true -ntp -P '!test' install cd dist/micro mvn -B -DskipTests=true -DskipITs=true -ntp -P docker deploy - name: Build Piranha Micro Profile image run: | mvn -B -Dmaven.javadoc.skip=true -DskipTests=true -DskipITs=true -ntp -P '!test' install cd dist/microprofile mvn -B -DskipTests=true -DskipITs=true -ntp -P docker deploy - name: Build Piranha Platform image run: | mvn -B -Dmaven.javadoc.skip=true -DskipTests=true -DskipITs=true -ntp -P '!test' install cd dist/platform mvn -B -DskipTests=true -DskipITs=true -ntp -P docker deploy - name: Build Piranha Server image run: | mvn -B -Dmaven.javadoc.skip=true -DskipTests=true -DskipITs=true -ntp -P '!test' install cd dist/server mvn -B -DskipTests=true -DskipITs=true -ntp -P docker deploy - name: Build Piranha Servlet image run: | mvn -B -Dmaven.javadoc.skip=true -DskipTests=true -DskipITs=true -ntp -P '!test' install cd dist/servlet mvn -B -DskipTests=true -DskipITs=true -ntp -P docker deploy - name: Build Piranha Web Profile image run: | mvn -B -Dmaven.javadoc.skip=true -DskipTests=true -DskipITs=true -ntp -P '!test' install cd dist/webprofile mvn -B -DskipTests=true -DskipITs=true -ntp -P docker deploy - name: Build with Maven run: | export MAVEN_OPTS='--add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.text=ALL-UNNAMED --add-opens=java.desktop/java.awt.font=ALL-UNNAMED' mvn -B -DstagingProgressTimeoutMinutes=10 -DskipTests=true -P 'release,!test' -ntp clean deploy env: MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} ================================================ FILE: .github/workflows/stale.yml ================================================ name: stale on: schedule: - cron: "0 0 * * *" workflow_dispatch: jobs: stale: if: github.repository == 'piranhacloud/piranha' runs-on: ubuntu-latest steps: - uses: actions/stale@v10 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: 'This issue is stale because it has been open 170 days with no activity. Remove stale label or comment or this will be closed in 10 days' stale-pr-message: 'This PR is stale because it has been open 170 days with no activity. Remove stale label or comment or this will be closed in 10 days' days-before-stale: 170 days-before-close: 10 ================================================ FILE: .github/workflows/tck-coreprofile.yml ================================================ name: tck-coreprofile on: schedule: - cron: "0 0 * * *" workflow_dispatch: inputs: branch: description: 'Branch / Tag' required: true default: 'current' jobs: annotations: if: github.repository == 'piranhacloud/piranha' runs-on: ${{ matrix.os }} strategy: matrix: java: [ '21' ] os: [ubuntu-latest] steps: - name: Checkout Sources uses: actions/checkout@v6 - name: Set up Java ${{ matrix.java }} uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: ${{ matrix.java }} - name: Run Annotations TCK run: | mvn -B -DskipTests=true -DskipITs=true -ntp install cd test/tck/coreprofile/annotations mvn -B -fae -ntp install - name: Annotation TCK Test Summary uses: test-summary/action@v2 with: paths: "test/tck/coreprofile/annotations/runner/target/failsafe-reports/*IT.xml" if: always() cdi: if: github.repository == 'piranhacloud/piranha' runs-on: ${{ matrix.os }} strategy: matrix: java: [ '21' ] os: [ubuntu-latest] steps: - name: Checkout Sources uses: actions/checkout@v6 - name: Set up Java ${{ matrix.java }} uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: ${{ matrix.java }} - name: Run TCK run: | mvn -B -DskipTests=true -DskipITs=true -ntp install cd test/tck/coreprofile/cdi mvn -B -fae -ntp install - name: Test Summary uses: test-summary/action@v2 with: paths: | cdi/runner/core/target/surefire-reports/junitreports/TEST-*.xml cdi/runner/model/target/surefire-reports/TEST-*.xml if: always() coreprofile: if: github.repository == 'piranhacloud/piranha' runs-on: ${{ matrix.os }} strategy: matrix: java: [ '21' ] os: [ubuntu-latest] steps: - name: Checkout Sources uses: actions/checkout@v6 - name: Set up Java ${{ matrix.java }} uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: ${{ matrix.java }} - name: Run TCK run: | mvn -B -DskipTests=true -DskipITs=true -ntp install cd test/tck/coreprofile/coreprofile mvn -B -fae -ntp install - name: Test Summary uses: test-summary/action@v2 with: paths: "test/tck/coreprofile/coreprofile/runner/target/failsafe-reports/**/TEST-*.xml" if: always() inject: if: github.repository == 'piranhacloud/piranha' runs-on: ${{ matrix.os }} strategy: matrix: java: [ '21' ] os: [ubuntu-latest] steps: - name: Checkout Sources uses: actions/checkout@v6 - name: Set up Java ${{ matrix.java }} uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: ${{ matrix.java }} - name: Run Inject TCK run: | mvn -B -DskipTests=true -DskipITs=true -ntp install cd test/tck/coreprofile/inject mvn -B -fae -ntp install - name: Test Summary uses: test-summary/action@v2 with: paths: "test/tck/coreprofile/inject/installer/target/tck/example/target/surefire-reports/TEST-*.xml" if: always() jsonb: if: github.repository == 'piranhacloud/piranha' runs-on: ${{ matrix.os }} strategy: matrix: java: [ '21' ] os: [ubuntu-latest] steps: - name: Checkout Sources uses: actions/checkout@v6 - name: Set up Java ${{ matrix.java }} uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: ${{ matrix.java }} - name: Run JSON Binding TCK run: | mvn -B -DskipTests=true -DskipITs=true -ntp install cd test/tck/coreprofile/jsonb mvn -B -fae -ntp install - name: Test Summary uses: test-summary/action@v2 with: paths: "test/tck/coreprofile/jsonb/installer/target/tck/bin/target/surefire-reports/TEST-*.xml" if: always() jsonp: if: github.repository == 'piranhacloud/piranha' runs-on: ${{ matrix.os }} strategy: matrix: java: [ '21' ] os: [ubuntu-latest] steps: - name: Checkout Sources uses: actions/checkout@v6 - name: Set up Java ${{ matrix.java }} uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: ${{ matrix.java }} - name: Run TCK run: | mvn -B -DskipTests=true -DskipITs=true -ntp install cd test/tck/coreprofile/jsonp mvn -B -fae -ntp install - name: Test Summary uses: test-summary/action@v2 with: paths: | test/tck/coreprofile/jsonp/installer/target/tck/bin/tck-tests/target/surefire-reports/TEST-*.xml test/tck/coreprofile/jsonp/installer/target/tck/bin/tck-tests-pluggability/target/surefire-reports/TEST-*.xml if: always() rest: if: github.repository == 'piranhacloud/piranha' runs-on: ${{ matrix.os }} strategy: matrix: java: [ '21' ] os: [ubuntu-latest] steps: - name: Checkout Sources uses: actions/checkout@v6 - name: Set up Java ${{ matrix.java }} uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: ${{ matrix.java }} - name: Run TCK run: | mvn -B -DskipTests=true -DskipITs=true -ntp install cd test/tck/coreprofile/rest mvn -B -fae -ntp install - name: Test Summary uses: test-summary/action@v2 id: test_summary with: paths: "test/tck/coreprofile/rest/runner/target/failsafe-reports/**/TEST-*.xml" output: test-summary.md if: always() - name: Job Summary run: | echo "${{ steps.test_summary.outputs.passed }} out of ${{ steps.test_summary.outputs.total }} tests passed" >> $GITHUB_STEP_SUMMARY if: always() result: if: github.repository == 'piranhacloud/piranha' runs-on: ubuntu-latest needs: [annotations, cdi, coreprofile, inject, jsonb, jsonp, rest] steps: - name: Check build results run: | result="${{ needs.build.result }}" if [[ $result == "success" || $result == "skipped" ]]; then echo "All TCK jobs were successful or skipped." exit 0 else echo "One or more TCK jobs failed." exit 0 fi ================================================ FILE: .github/workflows/trigger.yml ================================================ name: trigger on: workflow_dispatch: inputs: branch: description: 'Branch to release from' required: true default: 'current' version: description: 'Release version' required: true jobs: build: runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v6 with: token: ${{ secrets.GIT_PASSWORD }} - name: Release from given branch with given version run: | git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/* BRANCH=${{ github.event.inputs.branch }} TAG=${GITHUB_REF} VERSION=${{ github.event.inputs.version }} echo "Releasing $VERSION from $BRANCH branch" git checkout $BRANCH git checkout -b release mvn -B versions:set versions:commit -DnewVersion=$VERSION git config --global user.email "noreply@piranha.cloud" git config --global user.name "Automated release" git commit -a -m "Releasing version $VERSION from $BRANCH branch" git tag v$VERSION -f git push origin v$VERSION -f git checkout $BRANCH git branch -D release ================================================ FILE: .gitignore ================================================ .classpath .factorypath .DS_Store .project .settings/ .vscode dependency-reduced-pom.xml nb-configuration.xml nbactions.xml nbproject/ target/ .idea *.iml *.original~ dist/server/tmp/ dist/platform/tmp/ .flattened-pom.xml bin/ piranha.pid ================================================ FILE: CODE_CONVENTIONS.md ================================================ Eclipse/Sun code conventions with * Spaces only * Indentation size 4 spaces * Maximum line width 160 * Maximum width for comments 120 * No indent of Javadoc tags * No newline after @param tags ## Javadoc ### Parameters ``` @param a The first parameter. For an optimum result, this should be an odd number between 0 and 100. ``` ## Variable naming style Based on the advice from Uncle Bob's Clean Code, specifically: * No cryptic abbreviations like c, ta, rx, ct, except for the well-established i and j in loops * No variable names like ret, rvalue, result etc for variables that are returned from methods. Instead, they should be named after what they actually return. For example: Bad: ```java public Permissions getCallerPermission(....) { Permissions rvalue; // ton of code return rvalue; } ``` Good: ```java public Permissions getCallerPermissions(....) { Permissions callerPermissions; // ton of code return callerPermissions; } ``` * No Hungarian variations for collections like usrLst, usArray, arrUsers, UserCol, etc, and no such variation for elements of the collection like el, elm, usrEl, userElem, currentUsr, curUser, userCr, etc. Omit the Hungarian and use the element type name directly and the plural of that for the collection. For example: Bad: ```java for (User curUsr : colUser) { ... } ``` Good: ```java for (User user : users) { ... } ``` ## Conditional blocks ### Handle the short and fast error case for method parameters early instead of the happy path. For example: Bad: ```java public void foo(Bar bar) { if (bar != null) { // lots of code here } else { throw new IllegalStateException("Bar should not be null"); } } ``` Good: ```java public void foo(Bar bar) { if (bar == null) { throw new IllegalStateException("Bar should not be null"); } // lots of code here } ``` ### if/else blocks that return don't need to be if/else blocks. For example: Bad: ```java if (foo == something) { return somethingFoo; } else if (foo == somethingElse) { return somethingElseFoo; } ``` Good: ```java if (foo == something) { return somethingFoo; } if (foo == somethingElse) { return somethingElseFoo; } ``` ## Defaults ### Omit initialisation of instance variables to their default values. For example: Bad: ```java public class SomeClass { private int someNumber = 0; private Foo someFoo = null; private boolean isFoo = false; } ``` Good: ```java public class SomeClass { private int someNumber; private Foo someFoo; private boolean isFoo; } ``` ### Omit using the public modifier for interface methods. For example: Bad: ```java public interface MyInterface { public void MyMethod(); } ``` Good: ```java public interface MyInterface { void MyMethod(); } ``` ### Omit unnecessary final modifiers on local and method parameters Bad: ```java public void foo(final Bar bar) { // lots of code here } ``` Good: ```java public void foo(Bar bar) { // lots of code here } ``` ### Omit unnecessary usage of `this` Bad: ```java public Foo getFoo() { return this.foo; } ``` Good: ```java public Foo getFoo() { return foo; } ``` ### Omit unnecessary braces. For example: Bad ```java return (1); ``` Good ```java return 1; ``` ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing How can I contribute to Piranha? Well it all depends on the level of involvement you are aiming for. The following comes to mind: 1. Write blog entries with example applications 2. File issues for bugs you encounter. 3. Answer questions that people file at the issue tracker. 4. Fork the repository and issue pull requests. Which ever level you pick we welcome you! ## Important notice Note if you file issues, answer questions and/or issue pull requests you agree that those contributions will be owned by Manorrock.com and that Manorrock.com can use those contributions in any manner Manorrock.com so desires. ## Building and testing Piranha locally ### Building Piranha and running the tests ``` mvn clean install ``` If you do not want the tests to run use: ``` mvn -DskipTests clean install ``` ### Running a singular test To run a singular test pass in `-Dtest=expression`, see the `surefire` plugin documentation for more information. ### Running our more complex tests Our more complex tests are in the `test` profile which we do not release as part of a release because these modules only test functionality. You can run our more complex tests using: ``` mvn -P test clean install ``` ### Run the external tests (including TCKs) To run all the external tests use: ``` mvn -P external clean install ``` Or go into the directory of the external test you want to run and use: ``` mvn clean install ``` For the Servlet TCK if you want to run a singular test use `-Drun.test=expression`. See the example below. ``` mvn -Drun.test=com/sun/ts/tests/servlet/spec/errorpage/URLClient.java#servletToDifferentErrorPagesTest verify ``` ## Problems Support for Java modules has a feature gap in Eclipse and as such a workaround needs to be employed to make it work properly (June 2021). ================================================ FILE: LICENSE ================================================ Copyright (c) 2002-2021 Manorrock.com. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README.md ================================================ ![#piranhacloud](piranha_cloud.svg) # Piranha Project ![build status](https://github.com/piranhacloud/piranha/workflows/build/badge.svg) The Piranha Project delivers you with Cloud ready containers and useful add-on / integration modules. ## Quickstart Download a JAR based distribution from Maven Central. And run your web application using: ``` java -jar piranha-dist-XXX.jar --war-file your-webapplication.war ``` ## Getting Started To get started with Piranha please go [here](https://piranha.cloud/release/index.html). ## How do I contribute? See [Contributing](CONTRIBUTING.md) ## Our code of Conduct See [Code of Conduct](CODE_OF_CONDUCT.md) ## Important notice Note if you file issues or answer questions on the issue tracker and/or issue pull requests you agree that those contributions will be owned by Manorrock.com and that Manorrock.com can use those contributions in any manner Manorrock.com so desires. ================================================ FILE: RELEASE.md ================================================ # Release process The release process described below uses the 'current' branch. For patch releases see the [Patch releases](#patch-releases) section below ## On the 11th of the month 1. Create release-X.Y.Z branch from current 1. Trigger the current workflow against the release-X.Y.Z branch 1. Wait for the build on the release-X.Y.Z branch to complete 1. If the build was not successful 1. Checkout the release-X.Y.Z branch 1. Apply whatever fixes are needed 1. Commit and push them to release-X.Y.Z branch 1. Redo steps #2 and #3 1. On the current branch update the POMs to point to the next SNAPSHOT version ## On the 13th of the month 1. Trigger release using release-X.Y.Z as branch to release from 1. If release build fails 1. Remove the vX.Y.Z tag 1. Checkout the release-X.Y.Z branch 1. Apply whatever fixes are needed 1. Commit and push them to release-X.Y.Z 1. Retrigger the release 1. Trigger release docs workflow 1. Create release notes 1. Set correct tag for release notes 1. Make sure “Create a discussion for this release” is checked 1. Make sure Category is set to General 1. Publish release notes 1. Wait for release bits to show up on Maven Central 1. On the piranha_cloud Twitter announce that Piranha X.Y.Z has been released and make sure to thank our contributors. 1. Apply fixes from release-X.Y.Z branch to the current branch with a corresponding issue (if any). 1. Delete the release-X.Y.Z branch 1. Close the milestone on GitHub ## Patch releases If you are creating a patch release against a branch please follow all the steps above skipping the release docs workflow and replace current with the branch you are patching against where appropriate (without waiting a day in between). ================================================ FILE: SECURITY.md ================================================ # Security Policy ## Reporting a Vulnerability Please email info@piranha.cloud describing the issue (preferably with a reproducer and/or a fix) ================================================ FILE: arquillian/managed/pom.xml ================================================ 4.0.0 cloud.piranha.arquillian project 25.4.0-SNAPSHOT piranha-arquillian-managed jar Piranha - Arquillian - Managed org.apache.maven.plugins maven-surefire-plugin false me.alexpanov free-port-finder compile org.jboss.arquillian.container arquillian-container-spi compile org.jboss.shrinkwrap.descriptors shrinkwrap-descriptors-api-base org.jboss.arquillian.container arquillian-container-test-spi compile org.jboss.shrinkwrap.descriptors shrinkwrap-descriptors-api-base org.jboss.arquillian.protocol arquillian-protocol-servlet-jakarta compile org.jboss.shrinkwrap.descriptors shrinkwrap-descriptors-api-base org.jboss.arquillian.testenricher arquillian-testenricher-cdi-jakarta compile org.jboss.arquillian.testenricher arquillian-testenricher-ejb-jakarta compile org.jboss.arquillian.testenricher arquillian-testenricher-initialcontext compile org.jboss.arquillian.testenricher arquillian-testenricher-resource-jakarta compile org.jboss.shrinkwrap shrinkwrap-impl-base test org.jboss.shrinkwrap.descriptors shrinkwrap-descriptors-api-base test org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test default file:///tmp/piranha/arquillian/managed/ ================================================ FILE: arquillian/managed/src/main/java/cloud/piranha/arquillian/managed/ManagedPiranhaContainer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.arquillian.managed; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import org.jboss.arquillian.container.spi.client.container.DeployableContainer; import org.jboss.arquillian.container.spi.client.container.DeploymentException; import org.jboss.arquillian.container.spi.client.protocol.ProtocolDescription; import org.jboss.arquillian.container.spi.client.protocol.metadata.HTTPContext; import org.jboss.arquillian.container.spi.client.protocol.metadata.ProtocolMetaData; import org.jboss.arquillian.container.spi.client.protocol.metadata.Servlet; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.api.exporter.ZipExporter; import static java.lang.System.Logger.Level.INFO; import static java.lang.System.Logger.Level.WARNING; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; /** * The Managed Piranha container. * * @author Manfred Riem (mriem@manorrock.com) */ public class ManagedPiranhaContainer implements DeployableContainer { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(ManagedPiranhaContainer.class.getName()); /** * Stores the PID filename. */ private static final String PID_FILENAME = "tmp/piranha.pid"; /** * Stores the java.io.tmpdir constant. */ private static final String TMP_DIR = "java.io.tmpdir"; /** * Stores the 'Unable to create directories' message. */ private static final String UNABLE_TO_CREATE_DIRECTORIES = "Unable to create directories"; /** * Stores the configuration. */ private ManagedPiranhaContainerConfiguration configuration; /** * Stores the local repository directory. */ private File localRepositoryDir = new File(System.getProperty("user.home"), ".m2/repository"); /** * Stores the Piranha process. */ private Process process; /** * Default constructor. */ public ManagedPiranhaContainer() { } @Override public Class getConfigurationClass() { return ManagedPiranhaContainerConfiguration.class; } @Override public void setup(ManagedPiranhaContainerConfiguration configuration) { this.configuration = configuration; } @SuppressWarnings("exports") @Override public ProtocolDescription getDefaultProtocol() { return new ProtocolDescription(configuration.getProtocol()); } @SuppressWarnings("exports") @Override public ProtocolMetaData deploy(Archive archive) throws DeploymentException { LOGGER.log(INFO, "Deploying " + archive.getName()); ProtocolMetaData metadata = new ProtocolMetaData(); try { /* * Export the Archive into a WAR file. */ String warFileName = toWarFilename(archive); File runtimeDirectory = new File(System.getProperty(TMP_DIR), toAppName(warFileName)); runtimeDirectory.mkdirs(); File warFile = new File(runtimeDirectory, warFileName); archive.as(ZipExporter.class).exportTo(warFile, true); /* * Copy runtime JAR into the runtime diectory. */ String version = determineVersionToUse(); File piranhaJarFile = getPiranhaJarFile(version); copyPiranhaJarFile(runtimeDirectory, piranhaJarFile); /* * Start Piranha. */ startPiranha(runtimeDirectory, warFile); } catch (IOException ioe) { ioe.printStackTrace(); return null; } HTTPContext httpContext = new HTTPContext("localhost", configuration.getHttpPort()); httpContext.add(new Servlet( "ArquillianServletRunnerEE9", archive.getName().substring(0, archive.getName().lastIndexOf(".")))); metadata.addContext(httpContext); return metadata; } @Override public void undeploy(Archive archive) throws DeploymentException { LOGGER.log(INFO, "Undeploying " + archive.getName()); /* * Delete the PID file. */ File runtimeDirectory = new File(System.getProperty(TMP_DIR), toAppName(archive)); File pidFile = new File(runtimeDirectory, PID_FILENAME); if (pidFile.exists()) { try { Files.delete(pidFile.toPath()); } catch (IOException ioe) { LOGGER.log(WARNING, "Error deleting PID file", ioe); } } /* * Wait for 5 minutes at the most. */ if (process != null && process.isAlive()) { try { LOGGER.log(INFO, "Waiting for Piranha to be shutdown"); long startTime = System.currentTimeMillis(); process.waitFor(30, TimeUnit.SECONDS); Long finishTime = System.currentTimeMillis(); LOGGER.log(INFO, "Piranha has shutdown\n It took {0} milliseconds", finishTime - startTime); } catch (InterruptedException ie) { LOGGER.log(WARNING, "Piranha did not shutdown within time alloted"); LOGGER.log(WARNING, "Destroying Piranha process forcibly"); process.destroyForcibly(); } } /* * Delete the WAR file. */ File warFile = new File(runtimeDirectory, toWarFilename(archive)); warFile.delete(); } /** * Creates a WAR filename for the archive. * * @param archive the archive. * @return the WAR filename. */ private String toWarFilename(Archive archive) { String warFilename = archive.getName(); if (isEmpty(archive.getName())) { warFilename = "ROOT.war"; } return warFilename; } private String toAppName(String warFileName) { return warFileName.substring(0, warFileName.lastIndexOf(".")); } private String toAppName(Archive archive) { return toAppName(toWarFilename(archive)); } /** * Get the Piranha JAR file. * * @param version the version. * @return the zip file. * @throws IOException when an I/O error occurs. */ private File getPiranhaJarFile(String version) throws IOException { URL downloadUrl = createMavenCentralArtifactUrl( "cloud.piranha.dist", "piranha-dist-" + configuration.getDistribution(), version, "jar" ); String artifactPath = createArtifactPath( "cloud.piranha.dist", "piranha-dist-" + configuration.getDistribution(), version, "jar" ); File zipFile = new File(localRepositoryDir, artifactPath); if (!zipFile.exists() && !zipFile.getParentFile().mkdirs()) { LOGGER.log(WARNING, UNABLE_TO_CREATE_DIRECTORIES); } try (InputStream inputStream = downloadUrl.openStream()) { Files.copy(inputStream, zipFile.toPath(), REPLACE_EXISTING); } catch (IOException fnfe) { LOGGER.log(WARNING, "Could not download JAR file, defaulting back to local Maven repository"); } return new File(localRepositoryDir, artifactPath); } /** * Create artifact path. * * @param groupId the groupId. * @param artifactId the artifactId. * @param version the version * @param type the type. */ private String createArtifactPath(String groupId, String artifactId, String version, String type) { String artifactPathFormat = "%s/%s/%s/%s-%s.%s"; return String.format(artifactPathFormat, convertGroupIdToPath(groupId), artifactId, version, artifactId, version, type.toLowerCase()); } /** * Convert the groupId to path. * * @param groupId the groupId. * @return the path. */ private String convertGroupIdToPath(String groupId) { return groupId.replace('.', '/'); } /** * Create the Maven central artifact URL * * @param groupId the groupId. * @param artifactId the artifactId. * @param version the version * @param type the type. * @return the URL. * @throws IOException when an I/O error occurs. */ @SuppressWarnings("deprecation") private URL createMavenCentralArtifactUrl(String groupId, String artifactId, String version, String type) throws IOException { return new URL("https://repo1.maven.org/maven2/" + createArtifactPath(groupId, artifactId, version, type)); } /** * Determine what version of Piranha to use. * * @return the version. */ private String determineVersionToUse() { return getClass().getPackage().getImplementationVersion(); } /** * Kill any process currently listening on the given port. * * @param port the port to free up. */ private void killProcessOnPort(int port) { try { ProcessBuilder finder = new ProcessBuilder( "sh", "-c", "lsof -ti tcp:" + port + " -sTCP:LISTEN"); finder.redirectErrorStream(true); Process findProcess = finder.start(); String pids = new String(findProcess.getInputStream().readAllBytes()).trim(); findProcess.waitFor(5, TimeUnit.SECONDS); if (!pids.isEmpty()) { for (String pid : pids.split("\\s+")) { if (!pid.isEmpty()) { LOGGER.log(WARNING, "Port {0} is still in use by PID {1}, killing it", port, pid); new ProcessBuilder("kill", "-9", pid) .start() .waitFor(5, TimeUnit.SECONDS); } } // Brief pause so the OS releases the port Thread.sleep(200); } } catch (IOException | InterruptedException e) { LOGGER.log(WARNING, "Could not clean up port {0}: {1}", port, e.getMessage()); } } /** * Start Piranha. * * @param runtimeDirectory the runtime directory. * @param warFile the WAR filename. */ private void startPiranha(File runtimeDirectory, File warFile) throws IOException, DeploymentException { killProcessOnPort(configuration.getHttpPort()); File stalePidFile = new File(runtimeDirectory, PID_FILENAME); if (stalePidFile.exists()) { try { Files.delete(stalePidFile.toPath()); } catch (IOException ioe) { LOGGER.log(WARNING, "Could not delete stale PID file: {0}", ioe.getMessage()); } } List commands = new ArrayList<>(); StringBuilder classpath = new StringBuilder(); commands.add("java"); String[] jvmArgs = configuration.getJvmArguments().split("\\s+"); if (jvmArgs.length > 0) { for(int i=0; i 0 && jvmArgs[i] != null && jvmArgs[i-1].trim().equals("-cp")) { classpath.append(jvmArgs[i].trim()).append(File.pathSeparatorChar); } } } if (!configuration.getCallerName().isEmpty()) { commands.add("-Dio.piranha.identitystore.callers="); } if (configuration.isDebug()) { commands.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=localhost:9009"); } if (configuration.isSuspend()) { commands.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=localhost:9009"); } if (classpath.isEmpty()) { commands.add("-jar"); commands.add("piranha-" + configuration.getDistribution() + ".jar"); } else { commands.add("-cp"); commands.add(classpath.toString() + "piranha-" + configuration.getDistribution() + ".jar"); if (configuration.getDistribution().equals("coreprofile")) { commands.add("cloud.piranha.dist.coreprofile.CoreProfilePiranhaMain"); } if (configuration.getDistribution().equals("webprofile")) { commands.add("cloud.piranha.dist.webprofile.WebProfilePiranhaMain"); } } commands.add("--http-port"); commands.add(Integer.toString(configuration.getHttpPort())); commands.add("--war-file"); commands.add(warFile.getName()); commands.add("--write-pid"); String appName = toAppName(warFile.getName()); String appURL = "http://localhost:" + Integer.toString(configuration.getHttpPort()) + "/" + appName; File logFile = new File(runtimeDirectory, appName + ".log"); LOGGER.log(INFO, """ Starting Piranha Classpath: {0} Directory: {1} Log: {2} URL: {3} """, classpath.toString(), runtimeDirectory, logFile.getAbsolutePath(), appURL); process = new ProcessBuilder() .directory(runtimeDirectory) .command(commands) .redirectErrorStream(true) .redirectOutput(logFile) .start(); File pidFile = new File(runtimeDirectory, PID_FILENAME); int count = 0; LOGGER.log(INFO, "Waiting for Piranha to be ready"); while (!pidFile.exists() && process.isAlive()) { try { Thread.sleep(100); count++; } catch (InterruptedException ie) { } if (configuration.isSuspend()) { if (count % 500 == 0) { LOGGER.log(INFO, "Still waiting (infinite, because suspend on port 9009)"); } continue; } if (count % 20 == 0) { LOGGER.log(INFO, "Still waiting... ({0} of {1})", (count / 20), (1200 / 20)); } if (count == 1200) { LOGGER.log(WARNING, "Warning, PID file not seen!"); break; } } if (!process.isAlive()) { LOGGER.log(WARNING, "Piranha terminated during startup."); String msg = "Cannot start Piranha. \n"; if (process.getErrorStream() != null) { msg += Files.readString(logFile.toPath()); } throw new DeploymentException(msg); } LOGGER.log(INFO, "\n" + "Application is available at: " + appURL); } /** * Copy the Piranha JAR file. * * @param runtimeDirectory the runtime directory. * @param zipFile the zip file. */ private void copyPiranhaJarFile(File runtimeDirectory, File zipFile) throws IOException { if (!runtimeDirectory.exists() && !runtimeDirectory.mkdirs()) { System.err.println(UNABLE_TO_CREATE_DIRECTORIES); } Files.copy(zipFile.toPath(), Path.of(runtimeDirectory + "/piranha-" + configuration.getDistribution() + ".jar"), REPLACE_EXISTING); } /** * Is the string null or empty. * * @param string the string * @return true if it is, false otherwise. */ private boolean isEmpty(String string) { return string == null || string.isEmpty(); } } ================================================ FILE: arquillian/managed/src/main/java/cloud/piranha/arquillian/managed/ManagedPiranhaContainerConfiguration.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.arquillian.managed; import org.jboss.arquillian.container.spi.ConfigurationException; import org.jboss.arquillian.container.spi.client.container.ContainerConfiguration; import me.alexpanov.net.FreePortFinder; import static java.lang.System.Logger.Level.INFO; /** * The managed Piranha container configuration. * * The following system properties can be used to configure the Piranha * container from the command-line. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
System properties
namevaluenotes
piranha.debugThe boolean to start the Piranha process in debugging modenot enabled by default
piranha.distributionThe Piranha distribution to usecoreprofile by default
piranha.httpPortThe integer to select the HTTP port to use for the Piranha processif not set an unused port will be automatically chosen
piranha.callerNameThe username to seed into the in-memory identity storenot set by default
piranha.callerPasswordThe password for the caller to seed into the in-memory identity storenot set by default
piranha.callerGroupsThe comma-separated groups for the caller to seed into the in-memory identity storenot set by default
piranha.jvmArgumentsThe string with JVM arguments to pass to the Piranha processno additional JVM arguments are passed by default
piranha.protocolThe string with the Arquillian protocol to use when talking to the * Piranha processset to 'Servlet 6.0' by default
piranha.suspendthe boolean to start the Piranha process in suspend modenot enabled by default
* * @author Manfred Riem (mriem@manorrock.com) */ public class ManagedPiranhaContainerConfiguration implements ContainerConfiguration { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(ManagedPiranhaContainerConfiguration.class.getName()); /** * Stores the caller groups. */ private String callerGroups = System.getProperty("piranha.callerGroups", ""); /** * Stores the caller name. */ private String callerName = System.getProperty("piranha.callerName", ""); /** * Stores the caller password. */ private String callerPassword = System.getProperty("piranha.callerPassword", ""); /** * Stores the distribution to use. */ private String distribution = System.getProperty("piranha.distribution", "coreprofile"); /** * Stores the debug flag. */ private boolean debug = Boolean.parseBoolean(System.getProperty("piranha.debug", "false")); /** * Stores the HTTP port. */ private Integer httpPort = System.getProperty("piranha.httpPort") != null ? Integer.valueOf(System.getProperty("piranha.httpPort")) : null; /** * Stores the JVM arguments. */ private String jvmArguments = System.getProperty("piranha.jvmArguments", ""); /** * Stores the Arquillian protocol. */ private String protocol = System.getProperty("piranha.protocol", "Servlet 6.0"); /** * Stores the suspend flag. */ private boolean suspend = Boolean.parseBoolean(System.getProperty("piranha.suspend", "false")); /** * Constructor. */ public ManagedPiranhaContainerConfiguration() { } /** * Get the caller groups. * * @return the caller groups. */ public String getCallerGroups() { return callerGroups; } /** * Get the caller name. * * @return the caller name. */ public String getCallerName() { return callerName; } /** * Get the caller password. * * @return the caller password. */ public String getCallerPassword() { return callerPassword; } /** * Get the distribution. * * @return the distribution. */ public String getDistribution() { return distribution; } /** * Get the HTTP port. * * @return the HTTP port. */ public int getHttpPort() { if (httpPort == null) { httpPort = FreePortFinder.findFreeLocalPort(); } return httpPort; } /** * Get the JVM arguments. * * @return the JVM arguments. */ public String getJvmArguments() { return jvmArguments; } /** * Get the protocol. * * @return the protocol. */ public String getProtocol() { return protocol; } /** * Is the debug flag set. * * @return true if it is, false otherwise. */ public boolean isDebug() { return debug; } /** * Is the suspend flag set. * * @return the suspend flag. */ public boolean isSuspend() { return suspend; } /** * Set the caller groups. * * @param callerGroups the caller groups. */ public void setCallerGroups(String callerGroups) { this.callerGroups = callerGroups; } /** * Set the caller name. * * @param callerName the caller name. */ public void setCallerName(String callerName) { this.callerName = callerName; } /** * Set the caller password. * * @param callerPassword the caller password. */ public void setCallerPassword(String callerPassword) { this.callerPassword = callerPassword; } /** * Set the debug flag. * * @param debug the debug flag. */ public void setDebug(boolean debug) { this.debug = debug; } /** * Set the distribution. * * @param distribution the distribution. */ public void setDistribution(String distribution) { this.distribution = distribution; } /** * Set the HTTP port. * * @param httpPort the HTTP port. */ public void setHttpPort(int httpPort) { this.httpPort = httpPort; } /** * Set the JVM arguments. * * @param jvmArguments the JVM arguments. */ public void setJvmArguments(String jvmArguments) { this.jvmArguments = jvmArguments; } /** * Set the protocol. * * @param protocol the protocol. */ public void setProtocol(String protocol) { this.protocol = protocol; } /** * Set the suspend flag. * * @param suspend the suspend flag. */ public void setSuspend(boolean suspend) { this.suspend = suspend; } @Override public void validate() throws ConfigurationException { LOGGER.log(INFO, """ Using HTTP Port: {0} Using JVM arguments: {1} Using protocol: {2} Using debug: {3} Using suspend: {4} """, getHttpPort() + "", getJvmArguments(), getProtocol(), isDebug(), isSuspend()); } } ================================================ FILE: arquillian/managed/src/main/java/cloud/piranha/arquillian/managed/ManagedPiranhaContainerExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.arquillian.managed; import org.jboss.arquillian.container.spi.client.container.DeployableContainer; import org.jboss.arquillian.core.spi.LoadableExtension; /** * The Managed Piranha container extension. * * @author Manfred Riem (mriem@manorrock.com) */ public class ManagedPiranhaContainerExtension implements LoadableExtension { /** * Default constructor. */ public ManagedPiranhaContainerExtension() { } @SuppressWarnings("exports") @Override public void register(ExtensionBuilder extensionBuilder) { extensionBuilder.service(DeployableContainer.class, ManagedPiranhaContainer.class); } } ================================================ FILE: arquillian/managed/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ import cloud.piranha.arquillian.managed.ManagedPiranhaContainerExtension; import org.jboss.arquillian.core.spi.LoadableExtension; /** * This module delivers the managed Piranha container support for Arquillian. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.arquillian.managed { exports cloud.piranha.arquillian.managed; opens cloud.piranha.arquillian.managed; provides LoadableExtension with ManagedPiranhaContainerExtension; requires arquillian.container.spi; requires arquillian.core.spi; requires free.port.finder; requires shrinkwrap.api; } ================================================ FILE: arquillian/managed/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension ================================================ cloud.piranha.arquillian.managed.ManagedPiranhaContainerExtension ================================================ FILE: arquillian/managed/src/test/java/cloud/piranha/arquillian/managed/ManagedPiranhaContainerConfigurationTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.arquillian.managed; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * The JUnit tests for the ManagedPiranhaContainerConfiguration class. * * @author Manfred Riem (mriem@manorrock.com) */ class ManagedPiranhaContainerConfigurationTest { @Test void testGetHttpPortDefault() { System.clearProperty("piranha.httpPort"); ManagedPiranhaContainerConfiguration configuration = new ManagedPiranhaContainerConfiguration(); int port = configuration.getHttpPort(); assertTrue(port > 0, "The port should be a positive integer"); System.clearProperty("piranha.httpPort"); } @Test void testGetHttpPortSet() { System.setProperty("piranha.httpPort", "8080"); ManagedPiranhaContainerConfiguration configuration = new ManagedPiranhaContainerConfiguration(); int port = configuration.getHttpPort(); assertEquals(8080, port, "The port should be 8080"); System.clearProperty("piranha.httpPort"); } @Test void testGetHttpPortInvalid() { System.setProperty("piranha.httpPort", "invalid"); assertThrows(NumberFormatException.class, () -> { ManagedPiranhaContainerConfiguration configuration = new ManagedPiranhaContainerConfiguration(); configuration.getHttpPort(); }, "A NumberFormatException should be thrown for invalid port"); System.clearProperty("piranha.httpPort"); } @Test void testGetDistribution() { ManagedPiranhaContainerConfiguration config = new ManagedPiranhaContainerConfiguration(); assertEquals("coreprofile", config.getDistribution()); } @Test void testSetDistribution() { ManagedPiranhaContainerConfiguration config = new ManagedPiranhaContainerConfiguration(); config.setDistribution("webprofile"); assertEquals("webprofile", config.getDistribution()); } @Test void testGetHttpPort() { ManagedPiranhaContainerConfiguration config = new ManagedPiranhaContainerConfiguration(); assertTrue(config.getHttpPort() > 0); } @Test void testSetHttpPort() { ManagedPiranhaContainerConfiguration config = new ManagedPiranhaContainerConfiguration(); config.setHttpPort(8080); assertEquals(8080, config.getHttpPort()); } @Test void testGetJvmArguments() { ManagedPiranhaContainerConfiguration config = new ManagedPiranhaContainerConfiguration(); assertEquals("", config.getJvmArguments()); } @Test void testSetJvmArguments() { ManagedPiranhaContainerConfiguration config = new ManagedPiranhaContainerConfiguration(); config.setJvmArguments("-Xmx512m"); assertEquals("-Xmx512m", config.getJvmArguments()); } @Test void testGetProtocol() { ManagedPiranhaContainerConfiguration config = new ManagedPiranhaContainerConfiguration(); assertEquals("Servlet 6.0", config.getProtocol()); } @Test void testSetProtocol() { ManagedPiranhaContainerConfiguration config = new ManagedPiranhaContainerConfiguration(); config.setProtocol("Servlet 5.0"); assertEquals("Servlet 5.0", config.getProtocol()); } @Test void testIsDebug() { ManagedPiranhaContainerConfiguration config = new ManagedPiranhaContainerConfiguration(); assertFalse(config.isDebug()); } @Test void testSetDebug() { ManagedPiranhaContainerConfiguration config = new ManagedPiranhaContainerConfiguration(); config.setDebug(true); assertTrue(config.isDebug()); } @Test void testIsSuspend() { ManagedPiranhaContainerConfiguration config = new ManagedPiranhaContainerConfiguration(); assertFalse(config.isSuspend()); } @Test void testSetSuspend() { ManagedPiranhaContainerConfiguration config = new ManagedPiranhaContainerConfiguration(); config.setSuspend(true); assertTrue(config.isSuspend()); } @Test void testValidate() { ManagedPiranhaContainerConfiguration config = new ManagedPiranhaContainerConfiguration(); assertDoesNotThrow(config::validate); } } ================================================ FILE: arquillian/managed/src/test/java/cloud/piranha/arquillian/managed/ManagedPiranhaContainerExtensionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.arquillian.managed; import org.jboss.arquillian.core.spi.LoadableExtension.ExtensionBuilder; import org.jboss.arquillian.core.spi.context.Context; import org.junit.jupiter.api.Test; /** * The JUnit tests for the ManagedPiranhaContainerExtension class. * * @author Manfred Riem (mriem@manorrock.com) */ class ManagedPiranhaContainerExtensionTest { @Test void testRegister() { ExtensionBuilder extensionBuilder = new ExtensionBuilder() { @Override public ExtensionBuilder service(Class service, Class impl) { return this; } @Override public ExtensionBuilder override(Class service, Class oldServiceImpl, Class newServiceImpl) { throw new UnsupportedOperationException("Unimplemented method 'override'"); } @Override public ExtensionBuilder observer(Class handler) { throw new UnsupportedOperationException("Unimplemented method 'observer'"); } @Override public ExtensionBuilder context(Class context) { throw new UnsupportedOperationException("Unimplemented method 'context'"); } }; ManagedPiranhaContainerExtension extension = new ManagedPiranhaContainerExtension(); extension.register(extensionBuilder); } } ================================================ FILE: arquillian/managed/src/test/java/cloud/piranha/arquillian/managed/ManagedPiranhaContainerTest.java ================================================ package cloud.piranha.arquillian.managed; import java.io.File; import org.jboss.arquillian.container.spi.client.container.DeploymentException; import org.jboss.arquillian.container.spi.client.protocol.ProtocolDescription; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * The JUnit tests for the ManagedPiranhaContainer class. * * @author Manfred Riem (mriem@manorrock.com) */ class ManagedPiranhaContainerTest { /** * Test getContainerConfiguration method. */ @Test void testGetConfigurationClass() { ManagedPiranhaContainer container = new ManagedPiranhaContainer(); assertEquals(ManagedPiranhaContainerConfiguration.class, container.getConfigurationClass()); } /** * TEst getDefaulProtocol method. */ @Test void testGetDefaultProtocol() { ManagedPiranhaContainer container = new ManagedPiranhaContainer(); container.setup(new ManagedPiranhaContainerConfiguration()); ProtocolDescription protocolDescription = container.getDefaultProtocol(); assertEquals("Servlet 6.0", protocolDescription.getName()); } } ================================================ FILE: arquillian/managed/src/test/webapp/test/WEB-INF/web.xml ================================================ ================================================ FILE: arquillian/pom.xml ================================================ 4.0.0 cloud.piranha project 25.4.0-SNAPSHOT cloud.piranha.arquillian project pom Piranha - Arquillian The Piranha Arquillian Project delivers you with Arquillian support. managed server UTF-8 org.jboss.shrinkwrap.resolver shrinkwrap-resolver-bom ${shrinkwrap-resolver.version} test pom org.jboss.shrinkwrap.resolver shrinkwrap-resolver-depchain ${shrinkwrap-resolver.version} pom test org.jboss.arquillian arquillian-bom ${arquillian.version} pom import org.jboss.arquillian.jakarta arquillian-jakarta-bom ${arquillian-jakarta.version} pom import cloud.piranha bom ${project.version} pom import default file:///tmp/piranha/arquillian/ ================================================ FILE: arquillian/server/pom.xml ================================================ 4.0.0 cloud.piranha.arquillian project 25.4.0-SNAPSHOT piranha-arquillian-server Piranha - Arquillian - Server Piranha integration for the Arquillian Project. io.smallrye jandex cloud.piranha.micro piranha-micro-loader ${project.version} compile cloud.piranha.resource piranha-resource-api ${project.version} cloud.piranha.resource piranha-resource-impl ${project.version} cloud.piranha.resource piranha-resource-shrinkwrap ${project.version} org.jboss.shrinkwrap.resolver shrinkwrap-resolver-depchain pom org.jboss.arquillian.container arquillian-container-spi org.jboss.arquillian.container arquillian-container-test-spi org.jboss.arquillian.protocol arquillian-protocol-servlet-jakarta org.jboss.arquillian.testenricher arquillian-testenricher-cdi-jakarta org.jboss.arquillian.testenricher arquillian-testenricher-ejb-jakarta org.jboss.arquillian.testenricher arquillian-testenricher-resource-jakarta org.jboss.arquillian.testenricher arquillian-testenricher-initialcontext org.jboss.arquillian.junit arquillian-junit-container test org.junit.jupiter junit-jupiter-api test org.codehaus.mojo flatten-maven-plugin ossrh flatten.clean clean clean flatten process-resources flatten default file:///tmp/piranha/arquillian/server/ release org.apache.maven.plugins maven-gpg-plugin sign-artifacts verify sign ================================================ FILE: arquillian/server/src/main/java/cloud/piranha/arquillian/server/PiranhaServerDeployableContainer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.arquillian.server; import java.lang.System.Logger; import org.jboss.arquillian.container.spi.client.container.DeploymentException; import org.jboss.arquillian.container.spi.client.protocol.metadata.HTTPContext; import org.jboss.arquillian.container.spi.client.protocol.metadata.ProtocolMetaData; import org.jboss.arquillian.container.spi.client.protocol.metadata.Servlet; import org.jboss.shrinkwrap.api.Archive; import cloud.piranha.arquillian.server.PiranhaServerLoadableExtension.PiranhaServerContainerConfiguration; import cloud.piranha.micro.loader.MicroDeployOutcome; import cloud.piranha.micro.loader.MicroOuterDeployer; import static java.lang.System.Logger.Level.INFO; /** * The Piranha Micro Arquillian connector. * *

* This connector will start up an embedded Piranha Micro runtime in an isolated class loader for every application that is * deployed. * * @author Arjan Tijms * */ public class PiranhaServerDeployableContainer extends PiranhaServerLoadableExtension.PiranhaServerContainerBase { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(PiranhaServerDeployableContainer.class.getName()); /** * Stores the configuration. */ private PiranhaServerContainerConfiguration configuration; /** * Stores the outer deployer. */ private MicroOuterDeployer microOuterDeployer; /** * Constructor. */ public PiranhaServerDeployableContainer() { } @Override public void setup(PiranhaServerContainerConfiguration configuration) { this.configuration = configuration; configuration.validate(); } @Override public ProtocolMetaData deploy(Archive archive) throws DeploymentException { LOGGER.log(INFO, "Starting Piranha Micro"); microOuterDeployer = new MicroOuterDeployer(configuration); MicroDeployOutcome deployOutcome = microOuterDeployer.deploy(archive); HTTPContext httpContext = new HTTPContext("localhost", configuration.getPort()); String contextRoot = deployOutcome.getDeployedContextRoot(); if (contextRoot == null) { contextRoot = "/"; } for (String servletName : deployOutcome.getDeployedServlets()) { httpContext.add(new Servlet(servletName, contextRoot)); } ProtocolMetaData protocolMetaData = new ProtocolMetaData(); protocolMetaData.addContext(httpContext); return protocolMetaData; } @Override public void undeploy(Archive archive) throws DeploymentException { if (microOuterDeployer != null) { LOGGER.log(INFO, "Stopping Piranha Micro"); microOuterDeployer.stop(); } } } ================================================ FILE: arquillian/server/src/main/java/cloud/piranha/arquillian/server/PiranhaServerLoadableExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.arquillian.server; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.Random; import org.jboss.arquillian.container.spi.ConfigurationException; import org.jboss.arquillian.container.spi.client.container.ContainerConfiguration; import org.jboss.arquillian.container.spi.client.container.DeployableContainer; import org.jboss.arquillian.container.spi.client.protocol.ProtocolDescription; import org.jboss.arquillian.core.spi.LoadableExtension; import cloud.piranha.micro.loader.MicroConfiguration; /** * The extension sets up the Arquillian Server Connector * * @author Arjan Tijms * */ public class PiranhaServerLoadableExtension implements LoadableExtension { /** * Constructor. */ public PiranhaServerLoadableExtension() { } // Defines the deployable container used; PiranhaServerDeployableContainer.class // This is the actual "connector" that controls Piranha. @Override public void register(ExtensionBuilder builder) { builder.service(DeployableContainer.class, PiranhaServerDeployableContainer.class); } /** * The Piranha DeployableContainer. * *

     *  Defines the configuration class used: PiranhaServerContainerConfiguration.class
     *  Defines the protocol used: "Servlet 5.0"
     * 
* * @author Manfred Riem (mriem@manorrock.com) */ public abstract static class PiranhaServerContainerBase implements DeployableContainer { /** * Constructor. */ public PiranhaServerContainerBase() { } @Override public Class getConfigurationClass() { return PiranhaServerContainerConfiguration.class; } @Override public ProtocolDescription getDefaultProtocol() { return new ProtocolDescription("Servlet 5.0"); } } /** * The Piranha MicroConfiguration. * *
     *  Defines the configuration class to be essentially the same as MicroConfiguration.class
     * 
* * @author Manfred Riem (mriem@manorrock.com) */ public static class PiranhaServerContainerConfiguration extends MicroConfiguration implements ContainerConfiguration { /** * Highest port number that we'll try */ private static final int MAX_PORT_NUMBER = 65535; /** * Lowest port number that we'll try */ private static final int MIN_PORT_NUMBER = 1; /** * Stores whether to automatically find an available port. */ private boolean autoPort; /** * Helper class for finding a free port */ private PortFinder portFinder = new PortFinder(); /** * Initializes values from system properties or to default values */ public PiranhaServerContainerConfiguration() { super(); this.autoPort = Boolean.valueOf(System.getProperty("piranha.autoPort", "true")); } /** * Validates the configuration. * *

* Just calls {@link #postConstruct()} *

* * @throws ConfigurationException - never thrown */ @Override public void validate() throws ConfigurationException { postConstruct(); } /** * Initializes configuration after all configured values were loaded. * Computes generated configuration, e.g. a free port for autoPort * * @return the PiranhaServerContainerConfiguration. */ @Override public PiranhaServerContainerConfiguration postConstruct() { if (isAutoPort()) { setPort(portFinder.findFreePort(getPort())); } super.postConstruct(); return this; } /** * Are we automatically assigning a port? * * @return whether automatically assign port */ public boolean isAutoPort() { return autoPort; } /** * Set whether we are automatically assigning a port. * * @param autoPort Whether automatically assign port */ public void setAutoPort(boolean autoPort) { this.autoPort = autoPort; } private class PortFinder { /** * Find a random free local port. It's guaranteed that the returned port will be always random. * The initialPort parameter is only used as a base for the randomization and isn't returned * even if it's free. This is to prevent returning the same port if this method is called in parallel. * * @param initialPort The initial port to start searching from. * If 0, the default port 8080 will be used. * @return free port tha */ public int findFreePort(int initialPort) { int portCandidate = 8080; int numberOfAttempts = 100; boolean foundFreePort = false; final Random random = new Random(); if (initialPort > 0) { portCandidate = initialPort; } do { portCandidate += random.nextInt(100); foundFreePort = isFreePort(portCandidate); numberOfAttempts--; if (portCandidate > MAX_PORT_NUMBER) { portCandidate = portCandidate - MAX_PORT_NUMBER - 1 + MIN_PORT_NUMBER; } } while (!foundFreePort || numberOfAttempts <= 0); if (foundFreePort) { return portCandidate; } else { throw new RuntimeException("No free port found!"); } } private boolean isFreePort(int portCandidate) { try ( Socket clientSocket = new Socket("localhost", portCandidate)) { } catch (IOException clientEx) { // we cannot connect, the port isn't occupied try { // check whether this process can bind to the port - listen to it and then close it try (ServerSocket listeningSocket = new ServerSocket(portCandidate)) {} return true; } catch (IOException listenEx) { } } return false; } } } } ================================================ FILE: arquillian/server/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ import org.jboss.arquillian.core.spi.LoadableExtension; import cloud.piranha.arquillian.server.PiranhaServerLoadableExtension; /** * The Piranha Arquillian Server module. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.arquillian.server { provides LoadableExtension with PiranhaServerLoadableExtension; requires cloud.piranha.micro.loader; requires arquillian.container.spi; requires arquillian.core.spi; requires shrinkwrap.api; requires shrinkwrap.descriptors.api.base; } ================================================ FILE: arquillian/server/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension ================================================ cloud.piranha.arquillian.server.PiranhaServerLoadableExtension ================================================ FILE: bom/pom.xml ================================================ 4.0.0 cloud.piranha project 25.4.0-SNAPSHOT bom pom Piranha - BOM 2.1.3 3.0.0 3.1.0 3.0.0 3.1.1 4.0.1 6.0.1 4.1.0 4.1.2 2.0.1.MR 2.2.0 11.0.0 11.0.0 2.1.3 3.0.1 2.1.3 3.2.0 4.0.0 6.1.0 4.0.0 3.0.2 2.0.1 3.1.1 2.2.0 4.0.0 4.0.2 UTF-8 jakarta.activation jakarta.activation-api ${jakarta.activation-api.version} jakarta.annotation jakarta.annotation-api ${jakarta.annotation-api.version} jakarta.authentication jakarta.authentication-api ${jakarta.authentication-api.version} jakarta.authorization jakarta.authorization-api ${jakarta.authorization-api.version} jakarta.enterprise.concurrent jakarta.enterprise.concurrent-api ${jakarta.enterprise.concurrent-api.version} jakarta.enterprise jakarta.enterprise.cdi-api ${jakarta.enterprise.cdi-api.version} jakarta.enterprise jakarta.enterprise.cdi-el-api ${jakarta.enterprise.cdi-api.version} jakarta.platform jakarta.jakartaee-core-api ${jakarta.jakartaee-core-api.version} jakarta.platform jakarta.jakartaee-web-api ${jakarta.jakartaee-web-api.version} jakarta.ejb jakarta.ejb-api ${jakarta.ejb-api.version} jakarta.el jakarta.el-api ${jakarta.el-api.version} jakarta.faces jakarta.faces-api ${jakarta.faces-api.version} jakarta.inject jakarta.inject-api ${jakarta.inject-api.version} jakarta.interceptor jakarta.interceptor-api ${jakarta.interceptor-api.version} jakarta.json.bind jakarta.json.bind-api ${jakarta.json.bind-api.version} jakarta.json jakarta.json-api ${jakarta.json-api.version} jakarta.mail jakarta.mail-api ${jakarta.mail-api.version} jakarta.servlet.jsp jakarta.servlet.jsp-api ${jakarta.servlet.jsp-api.version} jakarta.persistence jakarta.persistence-api ${jakarta.persistence-api.version} jakarta.ws.rs jakarta.ws.rs-api ${jakarta.ws.rs-api.version} jakarta.security.enterprise jakarta.security.enterprise-api ${jakarta.security.enterprise-api.version} jakarta.servlet jakarta.servlet-api ${jakarta.servlet-api.version} jakarta.servlet.jsp.jstl jakarta.servlet.jsp.jstl-api ${jakarta.servlet.jsp.jstl-api.version} jakarta.transaction jakarta.transaction-api ${jakarta.transaction-api.version} jakarta.validation jakarta.validation-api ${jakarta.validation-api.version} jakarta.websocket jakarta.websocket-api ${jakarta.websocket-api.version} jakarta.websocket jakarta.websocket-client-api ${jakarta.websocket-api.version} jakarta.xml.bind jakarta.xml.bind-api ${jakarta.xml.bind-api.version} org.eclipse.microprofile.config microprofile-config-api ${microprofile-config-api.version} org.eclipse.microprofile.jwt microprofile-jwt-auth-api ${microprofile-jwt-auth-api.version} default file:///tmp/piranha/bom/ ================================================ FILE: core/api/pom.xml ================================================ 4.0.0 cloud.piranha.core project 25.4.0-SNAPSHOT piranha-core-api jar Piranha - Core - API jakarta.servlet jakarta.servlet-api compile cloud.piranha.resource piranha-resource-api ${project.version} compile default file:///tmp/piranha/core/api/ ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/AnnotationInfo.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import java.lang.reflect.AnnotatedElement; /** * The annotation information. * * @param the type. * @author Manfred Riem (mriem@manorrock.com) * @author Arjan Tijms */ public interface AnnotationInfo { /** * {@return the instance} */ T getInstance(); /** * {@return the target} */ AnnotatedElement getTarget(); /** * Get the target. * * @return the class. */ default Class getTargetType() { Class result = null; AnnotatedElement element = getTarget(); if (element instanceof Class clazz) { result = clazz; } return result; } } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/AnnotationManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.util.List; import java.util.Set; /** * The AnnotationManager API. * * @author Manfred Riem (mriem@manorrock.com) * @author Arjan Tijms */ public interface AnnotationManager { /** * Add the annotation. * * @param annotationInfo the annotation info. */ void addAnnotation(AnnotationInfo annotationInfo); /** * Add an instance. * * @param instanceClass the instance class. * @param implementingClass the implementing class. */ void addInstance(Class instanceClass, Class implementingClass); /** * Get the annotations. * * @param the type. * @param annotationClass the annotation class. * @return the typed list of annotations. */ List> getAnnotations(Class annotationClass); /** * Get the annotations. * * @param annotationClasses the annotation classes. * @return the list of annotations. */ List> getAnnotations(Class... annotationClasses); /** * Get the instances. * * @param the type. * @param instanceClass the instance class. * @return the typed list of instances. */ List> getInstances(Class instanceClass); /** * Get the instances. * * @param instanceClasses the instance classes. * @return the list of instances. */ List> getInstances(Class... instanceClasses); /** * Get the annotation for the annotation class and annotated element type. * * @param the type. * @param annotationClass the annotation class. * @param type the annotated element type. * @return the list of annotations. */ List> getAnnotationsByTarget(Class annotationClass, AnnotatedElement type); /** * Add an annotated class. * * @param annotationClass the annotation class on the annotated class. * @param clazz the annotated class. */ void addAnnotatedClass(Class annotationClass, Class clazz); /** * Get the annotated classes. * * @param annotationClass the annotation to inspect for. * @return the classes annotated with the given annotation (if any). */ Set> getAnnotatedClass(Class annotationClass); /** * Get annotated classes. * * @param annotationClasses the annotation classes to inspect for. * @return the classes annotated with the given annotations (if any). */ Set> getAnnotatedClasses(Class[] annotationClasses); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/AsyncDispatcher.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; /** * The AsyncDispatcher API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface AsyncDispatcher { /** * Dispatch. */ void dispatch(); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/AsyncManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; /** * The AsyncManager API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface AsyncManager { /** * {@return the async dispatcher} * @param webApplication the web application. * @param path the path. * @param asyncStartRequest the servlet request coming from a call to request.asyncStart. * @param asyncStartResponse the servlet response coming from a call to request.asyncStart. */ AsyncDispatcher getDispatcher(WebApplication webApplication, String path, ServletRequest asyncStartRequest, ServletResponse asyncStartResponse); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/AttributeManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import java.util.Enumeration; /** * The AttributeManager API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface AttributeManager { /** * Get the attribute. * * @param name the name. * @return the value. */ Object getAttribute(String name); /** * {@return the attribute names} */ Enumeration getAttributeNames(); /** * Remove the attribute. * * @param name the name. */ void removeAttribute(String name); /** * Set the attribute. * * @param name the name. * @param value the value. */ void setAttribute(String name, Object value); /** * Checks if this manager has the given attribute * @param name the name of the attribute to check for * @return true if this manager contains the attribute, false otherwise */ default boolean containsAttribute(String name) { return getAttribute(name) != null; } } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/AuthenticatedIdentity.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import java.security.Principal; import java.util.Set; /** * This interface is implemented by classes that represent the current authenticated identity. * *

* What current means here is context dependent. In a Jakarta Servlet application this refers * to the caller (user) details during a single HTTP request. * * @author Arjan Tijms * */ public interface AuthenticatedIdentity extends Principal { @Override default String getName() { if (getCallerPrincipal() == null) { return null; } return getCallerPrincipal().getName(); } /** * Returns the caller principal, which represents the primary name of the calling entity (aka the "caller") * to a server. * * @return the caller principal, or null if authentication has not (yet) completed successfully. */ Principal getCallerPrincipal(); /** * The groups the caller is in. * *

* If group to role mapping is not active (the default) groups are equal to roles. * * @return the set of groups the caller is in, never null. */ Set getGroups(); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/CurrentRequestHolder.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import jakarta.servlet.http.HttpServletRequest; /** * A holder that references the current HttpServletRequest in the request processing * pipeline. * *

* This structure allows the runtime to set what constitutes this current at any given * time. * * @author Arjan Tijms * */ public interface CurrentRequestHolder { /** * The request attribute under a holder implementation is stored in the request */ String CURRENT_REQUEST_ATTRIBUTE = CurrentRequestHolder.class.getName(); /** * Gets the current HttpServletRequest * * @param the actual implementation of the current HttpServletRequest * @return the current HttpServletRequest */ T getRequest(); /** * Sets the current HttpServletRequest * * @param request the current HttpServletRequest */ void setRequest(HttpServletRequest request); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/DispatcherManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import jakarta.servlet.RequestDispatcher; /** * The DispatcherManager API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface DispatcherManager { /** * Get the named request dispatcher. * * @param name the name. * @return the request dispatcher. */ RequestDispatcher getNamedDispatcher(String name); /** * Set the web application. * * @param webApplication the web application. */ void setWebApplication(WebApplication webApplication); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/ErrorPageManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import jakarta.servlet.http.HttpServletResponse; /** * The ErrorPageManager API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface ErrorPageManager { /** * Add an error page. * * @param statusCode the status code. * @param page the page. */ void addErrorPage(int statusCode, String page); /** * Add an error page. * * @param throwableClassName the throwable class name. * @param page the page. */ void addErrorPage(String throwableClassName, String page); /** * {@return the error page} * * @param throwable the throwable. * @param response the HTTP servlet response. */ String getErrorPage(Throwable throwable, HttpServletResponse response); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/FilterEnvironment.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import jakarta.servlet.Filter; import jakarta.servlet.FilterConfig; import jakarta.servlet.FilterRegistration.Dynamic; import jakarta.servlet.ServletException; /** * The environment of a Filter. * * @author Manfred Riem (mriem@manorrock.com) * @author Arjan Tijms */ public interface FilterEnvironment extends Dynamic, FilterConfig { /** * Defines the UNAVAILABLE constant. */ int UNAVAILABLE = -1; /** * {@return the filter} */ Filter getFilter(); /** * Initialize the filter. * * @throws ServletException when a servlet error occurs. */ void initialize() throws ServletException; /** * Set the class name. * * @param className the class name. */ void setClassName(String className); /** * Set the filter name. * * @param filterName the filter name. */ void setFilterName(String filterName); /** * Set status. * * @param status the status. */ void setStatus(int status); /** * {@return the web application} */ WebApplication getWebApplication(); /** * Set the web application. * * @param webApplication the web application. */ void setWebApplication(WebApplication webApplication); /** * Is async supported. * * @return true if it is, false otherwise. */ boolean isAsyncSupported(); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/FilterMapping.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import jakarta.servlet.DispatcherType; import static jakarta.servlet.DispatcherType.REQUEST; /** * The FilterMapping API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface FilterMapping { /** * {@return the filter name} */ String getFilterName(); /** * {@return the URL pattern} */ String getUrlPattern(); /** * {@return the dispatcher type} *

* The dispatcher type is the kind of dispatch that the filter * mapping applies to. Default is REQUEST, which represents * the request from the user to the system. *

*/ default DispatcherType getDispatcherType() { return REQUEST; } } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/FilterPriority.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; /** * The Filter Priority API allows filters that implement this interface to be given priority * depending on their priority value. * *

* Filters with a lower priority are called before filters with a higher priority. Filters * with an explicit priority are called before filters with no priority at all. * * @author Arjan Tijms * */ public interface FilterPriority { /** * Returns the priority of this filter in the filter chain. * * @return the priority value */ int getPriority(); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/HandlesTypesManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import java.util.Set; /** * The manager that delivers support for the HandlesTypes annotation. * *

* Whenever the onStartup method of a ServletContainerInitializer is called it * gets passed a set of classes that it expressed interest in. This manager * delivers the way a web application can vend that set of classes. See the * JavaDoc of the ServletContainerInitializer for more information about the * onStartup method. *

* * @author Manfred Riem (mriem@manorrock.com) */ public interface HandlesTypesManager { /** * Add the annotated class. * * @param annotationClass the annotation class. * @param annotatedClass the annotated class. */ void addAnnotatedClass(Class annotationClass, Class annotatedClass); /** * Add the extending class. * * @param baseClass the based class. * @param extendingClass the extending class. */ void addExtendingClass(Class baseClass, Class extendingClass); /** * Add the implementing class. * * @param interfaceClass the interface. * @param implementingClass the implementing class. */ void addImplementingClass(Class interfaceClass, Class implementingClass); /** * Get the annotated classes. * * @param annotationClass the annotation classes. * @return the annotated classes. */ Set> getAnnotatedClasses(Class annotationClass); /** * Get the extending classes. * * @param baseClass the base class. * @return the set of extending classes. */ Set> getExtendingClasses(Class baseClass); /** * Get the implementing classes. * * @param interfaceClass the interface class. * @return the set of implementing classes. */ Set> getImplementingClasses(Class interfaceClass); /** * Get the set of classes that either are annotated with the given classes, * implement any of the given classes, or extend any of the given classes. * * @param classes the set of given classes. * @return the set of classes or null if none found. */ Set> getClasses(Set> classes); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/HttpHeader.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import java.util.Enumeration; /** * The HttpHeader API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface HttpHeader { /** * Add the value. * * @param value the value to add. */ void addValue(String value); /** * {@return the name} */ String getName(); /** * {@return the value} */ String getValue(); /** * {@return the values} */ Enumeration getValues(); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/HttpHeaderManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import java.util.Enumeration; /** * The HttpHeaderManager API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface HttpHeaderManager { /** * Add the header. * * @param name the name. * @param value the value. */ void addHeader(String name, String value); /** * Contains the given header. * * @param name the header name. * @return true if there, false otherwise. */ boolean containsHeader(String name); /** * {@return the date header} * @param name the header name. * @throws IllegalArgumentException when the header could not be converted * to a date. */ long getDateHeader(String name) throws IllegalArgumentException; /** * Get the header. * * @param name the header name. * @return the header value. */ String getHeader(String name); /** * {@return the header names} */ Enumeration getHeaderNames(); /** * Get the headers. * * @param name the header name. * @return the header values. */ Enumeration getHeaders(String name); /** * Get the int header. * * @param name the header name. * @return the int value. * @throws NumberFormatException when the value could not be converted to an * int. */ int getIntHeader(String name) throws NumberFormatException; /** * Remove the given header. * * @param name the header name. */ void removeHeader(String name); /** * Set the header. * * @param name the name. * @param value the value (string). */ void setHeader(String name, String value); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/HttpSessionManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import java.util.EventListener; import java.util.Set; import jakarta.servlet.SessionCookieConfig; import jakarta.servlet.SessionTrackingMode; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; /** * The HttpSessionManager API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface HttpSessionManager { /** * Add a listener. * * @param the type. * @param listener the listener. */ void addListener(T listener); /** * Attribute added. * * @param session the HTTP session. * @param name the name. * @param value the value. */ void attributeAdded(HttpSession session, String name, Object value); /** * Attribute removed. * * @param session the HTTP session. * @param name the name. * @param value the value. */ void attributeRemoved(HttpSession session, String name, Object value); /** * Attribute replaced. * * @param session the HTTP session. * @param name the name. * @param oldValue the old value. * @param newValue the new value. */ void attributeReplaced(HttpSession session, String name, Object oldValue, Object newValue); /** * Change the session id and return it. * * @param request the request. * @return the session id. */ String changeSessionId(HttpServletRequest request); /** * Create a session. * * @param request the request. * @return the session. */ HttpSession createSession(HttpServletRequest request); /** * Destroys a session. * * @param session the HTTP session. */ void destroySession(HttpSession session); /** * Encode the redirect URL. * * @param response the HTTP servlet response. * @param url the URL. * @return the encode URL. */ String encodeRedirectURL(HttpServletResponse response, String url); /** * Encode the URL. * * @param response the HTTP servlet response. * @param url the URL. * @return the encoded URL. */ String encodeURL(HttpServletResponse response, String url); /** * {@return the default session tracking modes} */ Set getDefaultSessionTrackingModes(); /** * {@return the effective session tracking modes} */ Set getEffectiveSessionTrackingModes(); /** * {@return the session} * @param request the request. * @param currentSessionId the current session id. */ HttpSession getSession(HttpServletRequest request, String currentSessionId); /** * {@return the session cookie config} */ SessionCookieConfig getSessionCookieConfig(); /** * Get the session timeout (in minutes). * * @return the session timeout. */ int getSessionTimeout(); /** * Is the session manager handling this session? * * @param sessionId the session id. * @return true if there is a session with the given session id. */ boolean hasSession(String sessionId); /** * Set the session timeout (in minutes). * * @param timeout the timeout. */ void setSessionTimeout(int timeout); /** * Set the session tracking modes. * * @param sessionTrackingModes the session tracking modes. */ void setSessionTrackingModes(Set sessionTrackingModes); /** * Set the web application. * * @param webApplication the web application. */ void setWebApplication(WebApplication webApplication); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/JspManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import jakarta.servlet.ServletRegistration; import jakarta.servlet.descriptor.JspConfigDescriptor; /** * The JspManager API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface JspManager { /** * Add the JSP file. * * @param webApplication the web application. * @param servletName the servlet name. * @param jspFile the jsp file. * @return the servlet registration. */ ServletRegistration.Dynamic addJspFile(WebApplication webApplication, String servletName, String jspFile); /** * {@return the JSP config descriptor} */ JspConfigDescriptor getJspConfigDescriptor(); /** * Set the JspConfigDescriptor. * * @param jspConfigDescriptor the JspConfigDescriptor. */ void setJspConfigDescriptor(JspConfigDescriptor jspConfigDescriptor); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/LocaleEncodingManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; /** * The LocaleEncodingManager API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface LocaleEncodingManager { /** * Add a mapping between a locale and an encoding. * * @param locale the locale. * @param encoding the encoding. */ void addCharacterEncoding(String locale, String encoding); /** * Get the encoding for the locale. * * @param locale the locale. * @return the encoding, otherwise null. */ String getCharacterEncoding(String locale); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/ModuleLayerProcessor.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; /** * The module layer processor * @author Thiago Henrique Hupner */ public interface ModuleLayerProcessor { /** * Process the module layer according to the properties * @param controller the controller */ void processModuleLayerOptions(ModuleLayer.Controller controller); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/MultiPartManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import jakarta.servlet.ServletException; import jakarta.servlet.http.Part; import java.util.Collection; /** * The multi-part manager API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface MultiPartManager { /** * Get the parts. * * @param webApplication the web application. * @param request the web application request. * @return the parts. * @throws ServletException when the request is not a multipart/form-data request. */ Collection getParts(WebApplication webApplication, WebApplicationRequest request) throws ServletException; /** * Get the part. * * @param webApplication the web application. * @param request the web application request. * @param name the name of the part. * @return the part, or null if not found. * @throws ServletException when the request is not a multipart/form-data request. */ Part getPart(WebApplication webApplication, WebApplicationRequest request, String name) throws ServletException; } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/ObjectInstanceManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import jakarta.servlet.Filter; import jakarta.servlet.Servlet; import jakarta.servlet.ServletException; import java.util.EventListener; /** * The object instance manager API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface ObjectInstanceManager { /** * Create the filter. * * @param the type. * @param filterClass the Filter class. * @return the Filter. * @throws ServletException when a Servlet error occurs. */ T createFilter(Class filterClass) throws ServletException; /** * Create the listener. * * @param the type. * @param clazz the class. * @return the Listener. * @throws ServletException when a Servlet error occurs. */ T createListener(Class clazz) throws ServletException; /** * Create the servlet. * * @param the type. * @param clazz the Servlet class. * @return the Servlet. * @throws ServletException when a Servlet error occurs. */ T createServlet(Class clazz) throws ServletException; } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/Piranha.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; /** * The Piranha API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface Piranha { /** * Get the configuration. * * @return the configuration. */ PiranhaConfiguration getConfiguration(); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/PiranhaBuilder.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; /** * The PiranhaBuilder API. * * @author Manfred Riem (mriem@manorrock.com) * @param the concrete Piranha type. */ public interface PiranhaBuilder { /** * Build the Piranha instance. * * @return the Piranha instance. */ public T build(); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/PiranhaConfiguration.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import java.io.File; /** * Piranha configuration. * * @author Manfred Riem (mriem@manorrock.com) */ public interface PiranhaConfiguration { /** * Get the value as a boolean. * * @param key the key. * @param defaultValue the default value if the key is not found. * @return the value. */ boolean getBoolean(String key, boolean defaultValue); /** * Get the value as a class. * * @param key the key. * @return the value. */ Class getClass(String key); /** * Get the value as a File. * * @param key the key. * @return the value. */ File getFile(String key); /** * Get the value as an int. * * @param key the key. * @return the value. */ Integer getInteger(String key); /** * Get the value as a long. * * @param key the key. * @return the value. */ Long getLong(String key); /** * Get the value as a string. * * @param key the key. * @return the value (or null if not set). */ String getString(String key); /** * Set the boolean value. * * @param key the key. * @param value the boolean value. */ void setBoolean(String key, Boolean value); /** * Set the class value. * * @param key the key. * @param value the class value. */ void setClass(String key, Class value); /** * Set the File value. * * @param key the key. * @param value the value. */ void setFile(String key, File value); /** * Set the integer value. * * @param key the key. * @param value the value. */ void setInteger(String key, Integer value); /** * Set the long value. * * @param key the key. * @param value the value. */ void setLong(String key, Long value); /** * Set the string value. * * @param key the key. * @param value the value. */ void setString(String key, String value); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/SecurityConstraint.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import java.util.ArrayList; import java.util.List; /** * A security constraint. * * @author Manfred Riem (mriem@manorrock.com) */ public class SecurityConstraint { /** * Stores the role names. */ private List roleNames; /** * Stores the security web resource collections. */ private List securityWebResourceCollections; /** * Stores the transport guarantee. */ private String transportGuarantee; /** * Constructor. */ public SecurityConstraint() { roleNames = new ArrayList<>(); securityWebResourceCollections = new ArrayList<>(); transportGuarantee = "NONE"; } /** * Get the role names. * * @return the role names. */ public List getRoleNames() { return roleNames; } /** * Get the security web resource collection. * * @return the security web resource collection. */ public List getSecurityWebResourceCollections() { return securityWebResourceCollections; } /** * Get the transport guarantee. * * @return the transport guarantee. */ public String getTransportGuarantee() { return transportGuarantee; } /** * Set the role names. * * @param roleNames the role names. */ public void setRoleNames(List roleNames) { this.roleNames = roleNames; } /** * Set the security web resource collections. * * @param securityWebResourceCollections the security web resource collections. */ public void setSecurityWebResourceCollections( List securityWebResourceCollections) { this.securityWebResourceCollections = securityWebResourceCollections; } /** * Set the transport guarantee. * * @param transportGuarantee the transport guarantee. */ public void setTransportGuarantee(String transportGuarantee) { this.transportGuarantee = transportGuarantee; } } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/SecurityManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; /** * The SecurityManager API. * * @author Manfred Riem (mriem@manorrock.com) * @author Arjan Tijms */ public interface SecurityManager { /** * The enum that qualifies the source of the authentication. */ enum AuthenticateSource { /** * The container / runtime calls authenticate before a request */ PRE_REQUEST_CONTAINER, /** * The user (code) has programmatically called authenticate */ MID_REQUEST_USER } /** * Method that bypasses the authentication mechanism installed by the * authentication manager and directly invokes an identity store. */ interface UsernamePasswordLoginHandler { /** * Login. * * @param request the request. * @param username the username. * @param password the password. * @return the authenticated identity. */ AuthenticatedIdentity login(HttpServletRequest request, String username, String password); } /** * Authenticate the request. * * @param request the request. * @param response the response. * @return true if authenticated. * @throws IOException when an I/O error occurs. * @throws ServletException when a servlet error occurs. */ boolean authenticate(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException; /** * Authenticate the request. * * @param request the request. * @param response the response. * @param source the source or moment from where this authenticate method is * called * @return true if authenticated. * @throws IOException when an I/O error occurs. * @throws ServletException when a servlet error occurs. */ default boolean authenticate(HttpServletRequest request, HttpServletResponse response, AuthenticateSource source) throws IOException, ServletException { // By default, source and mandatory directive are ignored, and semantics for the 2 parameter // version hold. // The 2 parameter version is expected to be essentially source = MID_REQUEST_USER return authenticate(request, response); } /** * Declare roles. * * @param roles the roles. */ void declareRoles(String[] roles); /** * Declare roles. * * @param roles the roles. */ default void declareRoles(Collection roles) { if (roles != null) { declareRoles(roles.toArray(String[]::new)); } } /** * Get the auth method. * * @return the auth method. */ default String getAuthMethod() { return null; } /** * Gets the request object the security system wants to put in place. * *

* This method allows the security system (or authentication module being * delegated to) a custom or, more likely, wrapped request. * * @param request the request. * @param response the response. * @return a request object that the runtime should put into service */ default HttpServletRequest getAuthenticatedRequest(HttpServletRequest request, HttpServletResponse response) { return request; } /** * Gets the response object the security system wants to put in place. * *

* This method allows the security system (or authentication module being * delegated to) a custom or, more likely, wrapped response. * * @param request the request. * @param response the response. * @return a response object that the runtime should put into service */ default HttpServletResponse getAuthenticatedResponse(HttpServletRequest request, HttpServletResponse response) { return response; } /** * Get if we are denying uncovered HTTP methods. * * @return true if we are, false otherwise. */ default boolean getDenyUncoveredHttpMethods() { return false; } /** * Get the form error page. * * @return the form error page. */ default String getFormErrorPage() { return null; } /** * Get the form login page. * * @return the form login page. */ default String getFormLoginPage() { return null; } /** * Get the realm name. * * @return the realm name. */ default String getRealmName() { return null; } /** * Get the declared roles * * @return the roles */ Set getRoles(); /** * Get the security constraints. * * @return the security constraints. */ List getSecurityConstraints(); /** * Get the security role references. * * @return the security role references. */ Map> getSecurityRoleReferences(); /** * Get the handler that may be used by the login method to contact an * identity store. * * @return the UsernamePasswordLoginHandler or null if not set. */ default UsernamePasswordLoginHandler getUsernamePasswordLoginHandler() { return null; } /** * Get the web application. * * @return the web application. */ WebApplication getWebApplication(); /** * Check if the current caller (which can be the anonymous caller) is * authorized to access the requested resource. * *

* If the unauthenticated caller is authorized, then this means the resource * is public (aka unconstrained, aka unchecked), and the outcome of this * method MUST be consistent with * {@link #isRequestedResourcePublic(HttpServletRequest)}. * * * @param request the request. * @return true if the current caller is allowed to access the requested * resource, false otherwise */ default boolean isCallerAuthorizedForResource(HttpServletRequest request) { return true; } /** * Check if the current request adheres to the user data constraint, if any. * *

* In practice this means checking if HTTPS is used when so required by the * application. * * @param request the request. * @param response the response. * @return true if request adheres to constraints, false otherwise * @throws IOException when an I/O error occurs. * @throws ServletException when a servlet error occurs. */ default boolean isRequestSecurityAsRequired(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { return true; } /** * Check if the requested resource, represented by the request, is public or * not. * * @param request the request. * @return true if the requested resource can be accessed by public * (unauthenticated) callers, otherwise false */ default boolean isRequestedResourcePublic(HttpServletRequest request) { return true; } /** * Is the user in the specific role. * * @param request the request. * @param role the role. * @return true if in the role, false otherwise. */ boolean isUserInRole(HttpServletRequest request, String role); /** * Login. * * @param request the request. * @param username the username. * @param password the password. * @throws ServletException when unable to login. */ void login(HttpServletRequest request, String username, String password) throws ServletException; /** * Logout. * * @param request the request. * @param response the response. * @throws ServletException when a servlet error occurs. */ void logout(HttpServletRequest request, HttpServletResponse response) throws ServletException; /** * Gives the security system the opportunity to process the response after * the request (after the target resource has been invoked). * *

* Although this may be rare to used in practice, it allows for encryption * of the response, inserting security tokens, signing the response, etc. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. * @throws ServletException when a servlet error occurs. */ default void postRequestProcess(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { } /** * Set the auth method. * * @param authMethod the auth method. */ default void setAuthMethod(String authMethod) { } /** * Set if we are denying uncovered HTTP methods. * * @param denyUncoveredHttpMethods the boolean value. */ default void setDenyUncoveredHttpMethods(boolean denyUncoveredHttpMethods) { } /** * Set the form error page. * * @param formErrorPage the form error page. */ default void setFormErrorPage(String formErrorPage) { } /** * Set the form login page. * * @param formLoginPage the form login page. */ default void setFormLoginPage(String formLoginPage) { } /** * Set the realm name. * * @param realmName the realm name. */ default void setRealmName(String realmName) { } /** * Set the security constraints. * * @param securityConstraints the security constraints. */ void setSecurityConstraints(List securityConstraints); /** * Set the security role references. * * @param securityRoleReferences the security role references. */ void setSecurityRoleReferences(Map> securityRoleReferences); /** * Set the handler that may be used by the login method to contact an * identity store. * * @param usernamePasswordLoginHandler the handler */ default void setUsernamePasswordLoginHandler(UsernamePasswordLoginHandler usernamePasswordLoginHandler) { // do nothing, optional method } /** * Set the web application. * * @param webApplication the web application. */ void setWebApplication(WebApplication webApplication); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/SecurityRoleReference.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; /** * A security role ref(erence). * * @author Manfred Riem (mriem@manorrock.com) */ public class SecurityRoleReference { /** * Stores the role link. */ private String roleLink; /** * Stores the role name. */ private String roleName; /** * Constructor. */ public SecurityRoleReference() { } /** * Get the role link. * * @return the role link. */ public String getRoleLink() { return roleLink; } /** * Get the role name. * * @return the role name. */ public String getRoleName() { return roleName; } /** * Set the role link. * * @param roleLink the role link. */ public void setRoleLink(String roleLink) { this.roleLink = roleLink; } /** * Set the role name. * * @param roleName the role name. */ public void setRoleName(String roleName) { this.roleName = roleName; } } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/SecurityWebResourceCollection.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import java.util.ArrayList; import java.util.List; /** * A security web resource collection. * * @author Manfred Riem (mriem@manorrock.com) */ public class SecurityWebResourceCollection { /** * Stores the HTTP methods. */ private List httpMethods; /** * Stores the HTTP method omissions. */ private List httpMethodOmissions; /** * Stores the URL patterns. */ private List urlPatterns; /** * Constructor. */ public SecurityWebResourceCollection() { this.httpMethods = new ArrayList<>(); this.httpMethodOmissions = new ArrayList<>(); this.urlPatterns = new ArrayList<>(); } /** * Get the HTTP methods. * * @return the HTTP methods. */ public List getHttpMethods() { return httpMethods; } /** * Get the HTTP method omissions. * * @return the HTTP method omissions. */ public List getHttpMethodOmissions() { return httpMethodOmissions; } /** * Get the URL patterns. * * @return the URL patterns. */ public List getUrlPatterns() { return urlPatterns; } /** * Set the HTTP methods. * * @param httpMethods the HTTP methods. */ public void setHttpMethods(List httpMethods) { this.httpMethods = httpMethods; } /** * Set the HTTP method omissions. * * @param httpMethodOmissions the HTTP method omissions. */ public void setHttpMethodOmissions(List httpMethodOmissions) { this.httpMethodOmissions = httpMethodOmissions; } /** * Set the URL patterns. * * @param urlPatterns the URL patterns. */ public void setUrlPatterns(List urlPatterns) { this.urlPatterns = urlPatterns; } } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/ServletEnvironment.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import jakarta.servlet.MultipartConfigElement; import jakarta.servlet.Servlet; import jakarta.servlet.ServletConfig; import jakarta.servlet.ServletRegistration.Dynamic; /** * The environment for a Servlet. * * @author Manfred Riem (mriem@manorrock.com) */ public interface ServletEnvironment extends Dynamic, ServletConfig { /** * Defines the UNAVAILABLE constant. */ int UNAVAILABLE = -1; /** * {@return the load on startup} */ int getLoadOnStartup(); /** * {@return the multi-part config} */ MultipartConfigElement getMultipartConfig(); /** * {@return the servlet} */ Servlet getServlet(); /** * {@return the servlet class} */ Class getServletClass(); /** * {@return the status} */ int getStatus(); /** * {@return the web application} */ WebApplication getWebApplication(); /** * Is async supported. * * @return true if it is, false otherwise. */ boolean isAsyncSupported(); /** * Set the class name. * * @param className the class name. */ void setClassName(String className); /** * Set the servlet. * * @param servlet the servlet. */ void setServlet(Servlet servlet); /** * Set the status. * * @param status the status. */ void setStatus(int status); /** * The exception that caused this servlet to become unavailable * * @return the exception. */ Throwable getUnavailableException(); /** * Sets the exception that caused this servlet to become unavailable * * @param unavailableException the unavailable exception. */ void setUnavailableException(Throwable unavailableException); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/ServletInvocation.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import static cloud.piranha.core.api.ServletEnvironment.UNAVAILABLE; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletConfig; import java.util.List; /** * The ServletInvocation API. * *

* This type holds data necessary to invoke a Servlet. * * * @author Arjan Tijms * */ public interface ServletInvocation { /** * The original path used to base the Servlet invocation on. * * @return the full invocation path */ String getInvocationPath(); /** * {@return the servlet name} */ String getServletName(); /** * {@return the servlet path} */ String getServletPath(); /** * Gets the original servlet path. * *

* The original servlet path differs from the servlet path if the * invocation locator has found an alternative resource than the one * requested. This is typically the case for welcome pages. * * @return the original servlet path. */ default String getOriginalServletPath() { return getServletPath(); } /** * {@return the path info} */ String getPathInfo(); /** * {@return the web application request mapping} */ WebApplicationRequestMapping getApplicationRequestMapping(); /** * {@return the servlet environment} */ ServletEnvironment getServletEnvironment(); /** * {@return the filter environments} */ List getFilterEnvironments(); /** * {@return the filter chain} */ FilterChain getFilterChain(); /** * {@return whether this invocation is obtained from a getNamedDispatcher} */ default boolean isFromNamed() { return false; } /** * Set the from named flag. * * @param fromNamed whether this invocation is obtained from a getNamedDispatcher */ default void setFromNamed(boolean fromNamed) { // Ignore } /** * Do we have a servlet. * * @return true if we do, false otherwise. */ default boolean hasServlet() { return getServletEnvironment() != null && getServletEnvironment().getServlet() != null; } /** * Do we have a filter. * * @return true if we do, false otherwise. */ default boolean hasFilter() { return getFilterEnvironments() != null; } /** * Is the servlet unavailable. * * @return true if it is, false otherwise. */ default boolean isServletUnavailable() { return getServletEnvironment() != null && getServletEnvironment().getStatus() == UNAVAILABLE; } /** * Can we invoke. * * @return true if we can, false otherwise. */ default boolean canInvoke() { return hasServlet() || hasFilter(); } /** * {@return the servlet configuration} */ default ServletConfig getServletConfig() { if (!hasServlet()) { return null; } return getServletEnvironment().getServlet().getServletConfig(); } } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/ServletRequestManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import jakarta.servlet.ServletRequest; import java.util.EventListener; import jakarta.servlet.http.HttpServletRequest; /** * The ServletRequest manager. * * @author Arjan Tijms * @author Manfred Riem (mriem@manorrock.com) */ public interface ServletRequestManager { /** * Add a listener. * * @param the type. * @param listener the listener. */ void addListener(T listener); /** * Fire the attribute added event. * * @param request the request. * @param name the name. * @param value the value. */ void attributeAdded(HttpServletRequest request, String name, Object value); /** * Fire the attribute removed event. * * @param request the request. * @param name the name. * @param value the value */ void attributeRemoved(HttpServletRequest request, String name, Object value); /** * Fire the attribute replaced event. * * @param request the request. * @param name the name. * @param value the value. */ void attributeReplaced(HttpServletRequest request, String name, Object value); /** * Fire the request destroyed event. * * @param request the request. */ void requestDestroyed(ServletRequest request); /** * Fire the request initialized event. * * @param request the request. */ void requestInitialized(ServletRequest request); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/WebApplication.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import cloud.piranha.resource.api.Resource; import jakarta.servlet.DispatcherType; import jakarta.servlet.FilterRegistration; import jakarta.servlet.Servlet; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRegistration; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.descriptor.JspConfigDescriptor; import java.io.IOException; import java.util.Collection; import java.util.EnumSet; import java.util.List; import java.util.Set; /** * The WebApplication API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface WebApplication extends ServletContext { /** * Status enum. */ enum Status { /** * Web application is in setup mode. */ SETUP, /** * Web application has processed system declared constructs (e.g. * web.xml / web-fragment.xml and Servlet annotations). */ DECLARED, /** * Web application is initialized. */ INITIALIZED, /** * Web application is servicing. */ SERVICING, /** * Web application is in error state. */ ERROR } /** * Add a mapping for the given filter. * * @param filterName the filter name. * @param urlPatterns the URL patterns. * @return the possible empty set of already mapped URL patterns. * @see FilterRegistration#addMappingForUrlPatterns(EnumSet, boolean, * String...) */ default Set addFilterMapping(String filterName, String... urlPatterns) { return addFilterMapping(filterName, true, urlPatterns); } /** * Add a mapping for the given filter. * * @param filterName the filter name. * @param isMatchAfter true to call the filter this mapping applies to after * declared ones, false to call it before declared ones. * @param urlPatterns the URL patterns. * @return the possible empty set of already mapped URL patterns. * @see FilterRegistration#addMappingForUrlPatterns(EnumSet, boolean, * String...) */ default Set addFilterMapping(String filterName, boolean isMatchAfter, String... urlPatterns) { return addFilterMapping(null, filterName, isMatchAfter, urlPatterns); } /** * Add a mapping for the given filter. * * @param dispatcherTypes the dispatcher types. Can be null to use default * DispatcherType.REQUEST. * @param filterName the filter name. * @param isMatchAfter true to call the filter this mapping applies to after * declared ones, false to call it before declared ones. * @param urlPatterns the URL patterns. * @return the possible empty set of already mapped URL patterns. * @see FilterRegistration#addMappingForUrlPatterns(EnumSet, boolean, * String...) */ Set addFilterMapping(Set dispatcherTypes, String filterName, boolean isMatchAfter, String... urlPatterns); /** * Add a servlet container initializer. * * @param className the class name. */ void addInitializer(String className); /** * Add a servlet container initializer. * * @param servletContainerInitializer the servletContainerInitializer * instance */ void addInitializer(ServletContainerInitializer servletContainerInitializer); /** * Add the mime type. * * @param extension the extension (without the dot). * @param mimeType the mime type to return. */ void addMimeType(String extension, String mimeType); /** * Add the resource. * * @param resource the resouce. */ void addResource(Resource resource); /** * Add a mapping for the given servlet. * * @param servletName the servlet name. * @param urlPatterns the URL patterns. * @return the possible empty set of already mapped URL patterns. * @see ServletRegistration#addMapping(String...) */ Set addServletMapping(String servletName, String... urlPatterns); /** * Destroy the web application. * * @return the web application. */ WebApplication destroy(); /** * Get the default servlet. * * @return the default Servlet. */ Servlet getDefaultServlet(); /** * Gets the ServletContainerInitializers * * @return list of ServletContainerInitializers */ List getInitializers(); /** * Get the web application manager. * * @return the web application manager. */ WebApplicationManager getManager(); /** * Get the mappings for a particular servlet. * * @param servletName the servlet name. * @return the possible empty set of mapped URL patterns. * @see ServletRegistration#getMappings() */ Collection getMappings(String servletName); /** * Get the request. * * @param response the response. * @return the request. */ ServletRequest getRequest(ServletResponse response); /** * Get the response. * * @param request the request. * @return the response. */ ServletResponse getResponse(ServletRequest request); /** * Returns the unique Id of this web application corresponding to this * ServletContext. * * @return the servlet context id. */ default String getServletContextId() { return getVirtualServerName() + " " + getContextPath(); } /** * Get the status. * * @return the status. */ Status getStatus(); /** * Initialize the web application. * * @return the web application. */ WebApplication initialize(); /** * Is the application distributable. * * @return true if it is, false otherwise. */ boolean isDistributable(); /** * Is the web application initialized. * * @return true if it is, false otherwise. */ boolean isInitialized(); /** * Is the web application metadata complete. * * @return true if it is, false otherwise. */ boolean isMetadataComplete(); /** * Is the web application currently servicing requests. * * @return true if it is, false otherwise. */ boolean isServicing(); /** * Link the request and response. * * @param request the request. * @param response the response. */ void linkRequestAndResponse(ServletRequest request, ServletResponse response); /** * Remove a mapping for a servlet. * * @param urlPattern the URL pattern * @return the Servlet name the pattern was mapped to, or null if no prior * mapping. */ String removeServletMapping(String urlPattern); /** * Service the request. * * @param request the request. * @param response the response. * @throws ServletException when a servlet error occurs. * @throws IOException when an I/O error occurs. */ void service(ServletRequest request, ServletResponse response) throws ServletException, IOException; /** * Set the class loader. * * @param classLoader the class loader. */ void setClassLoader(ClassLoader classLoader); /** * Set the context path. * * @param contextPath the context path. */ void setContextPath(String contextPath); /** * Set the default servlet. * * @param defaultServlet the default servlet. */ void setDefaultServlet(Servlet defaultServlet); /** * Set if the web application is distributable. * * @param distributable the distributable flag. */ void setDistributable(boolean distributable); /** * Set the effective major version. * * @param version the effective major version. */ void setEffectiveMajorVersion(int version); /** * Set the effective minor version. * * @param version the effective minor version. */ void setEffectiveMinorVersion(int version); /** * Set the JSP config descriptor. * * @param descriptor the descriptor. */ void setJspConfigDescriptor(JspConfigDescriptor descriptor); /** * Set the metadata complete flag. * * @param metadataComplete the metadata complete flag. */ void setMetadataComplete(boolean metadataComplete); /** * Set the servlet context name. * * @param servletContextName the servlet context name. */ void setServletContextName(String servletContextName); /** * Set the status. * * @param status the status. */ void setStatus(Status status); /** * Set the virtual server name. * * @param virtualServerName the virtual server name. */ void setVirtualServerName(String virtualServerName); /** * Set the web application request mapper. * * @param webApplicationRequestMapper the web application request mapper. */ void setWebApplicationRequestMapper(WebApplicationRequestMapper webApplicationRequestMapper); /** * Start servicing. * * @return the web application. */ WebApplication start(); /** * Stop servicing. * * @return the web application. */ WebApplication stop(); /** * Unlink the request and response. * * @param request the request. * @param response the response. */ void unlinkRequestAndResponse(ServletRequest request, ServletResponse response); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/WebApplicationClassLoader.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import cloud.piranha.resource.api.ResourceManagerClassLoader; /** * The WebApplicationClassLoader API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface WebApplicationClassLoader extends ResourceManagerClassLoader { } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/WebApplicationExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; /** * The web application extension API. * *

* A web application extension can be used to automatically configure the web * application according to what the extension delivers, or you can use it as a * way to compose extensions together in one extension. *

* * @author Manfred Riem (mriem@manorrock.com) */ public interface WebApplicationExtension { /** * Configure the web application. * * @param webApplication the web application to configure. */ default void configure(WebApplication webApplication) { } /** * Extend the web application. * * @param context the context. */ default void extend(WebApplicationExtensionContext context) { } } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/WebApplicationExtensionContext.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; /** * The web application extension context API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface WebApplicationExtensionContext { /** * Add the extension to the web application. * *

* This will add this extension to the list of extensions that will be used * when configuring the web application. *

* * @param extension the extension. */ void add(Class extension); /** * Add the extension to the web application. * *

* This will add this extension to the list of extensions that will be used * when configuring the web application. *

* * @param extension the extension. */ void add(WebApplicationExtension extension); /** * Remove the extension from the web application. * *

* This will remove the extension from the list of extensions that will be * used when configuring the web application. *

*

* NOTE this will ONLY remove this extension. Any extensions that were added * to the list by this extension BEFORE it was removed will NOT be removed * from the list of extensions. This is by design. *

* * @param extension the extension. */ void remove(Class extension); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/WebApplicationInputStream.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import jakarta.servlet.ReadListener; import jakarta.servlet.ServletInputStream; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * The web application input stream. * * @author Manfred Riem (mriem@manorrock.com) */ public abstract class WebApplicationInputStream extends ServletInputStream implements Runnable { /** * Stores the finished flag. */ protected boolean finished; /** * Stores the read index. */ protected int index; /** * Stores the input stream. */ protected InputStream inputStream; /** * Stores the read listener. */ protected ReadListener readListener; /** * Stores the read listener lock. */ protected Lock readListenerLock = new ReentrantLock(); /** * Stores the web application request. */ protected WebApplicationRequest webApplicationRequest; /** * Constructor. */ public WebApplicationInputStream() { inputStream = new ByteArrayInputStream(new byte[0]); } @Override public void close() throws IOException { super.close(); finished = true; } /** * Get the read listener. * * @return the read listener, or null if not set. */ public ReadListener getReadListener() { return readListener; } @Override public boolean isFinished() { return finished; } @Override public boolean isReady() { boolean ready; try { readListenerLock.lock(); ready = inputStream.available() > 0; } catch (IOException ioe) { ready = false; } finally { readListenerLock.unlock(); } return ready; } @Override public int read() throws IOException { int read; if (readListener == null) { if (finished || webApplicationRequest.getContentLength() == 0) { return -1; } read = readWithTimeout(); index++; if (index == webApplicationRequest.getContentLength() || read == -1) { finished = true; } } else { read = readWithTimeout(); } return read; } /** * Read with a timeout. * * @return the byte read or -1 if the end has been reached (or a timeout * occurred). */ private int readWithTimeout() { int read = -1; /* * Because we do not know if the underlying inputstream can * block indefinitely we make sure we read from the inputstream * with a timeout so we do not block the thread indefinitely. * * If we do not get a read to succeed within the 30 seconds * timeout we return -1 to indicate we assume the end of the * stream has been reached. */ ExecutorService executor = Executors.newSingleThreadExecutor(); Callable readTask = () -> { return inputStream.read(); }; Future future = executor.submit(readTask); try { read = future.get(30, TimeUnit.SECONDS); } catch (TimeoutException | InterruptedException | ExecutionException e) { read = -1; } finally { executor.shutdown(); } return read; } @Override public void run() { while (true) { try { if (isReady()) { try { readListenerLock.lock(); readListener.onDataAvailable(); } finally { readListenerLock.unlock(); } } if (!finished) { try { Thread.sleep(500); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } if (finished) { try { readListenerLock.lock(); readListener.onAllDataRead(); break; } finally { readListenerLock.unlock(); } } } catch (IOException ioe) { readListener.onError(ioe); break; } } } /** * Set the input stream. * * @param inputStream the input stream. */ public void setInputStream(InputStream inputStream) { this.inputStream = inputStream; } @Override public void setReadListener(ReadListener readListener) { if (readListener == null) { throw new NullPointerException("Read listener cannot be null"); } if (this.readListener != null) { throw new IllegalStateException("Read listener can only be set once"); } if (!webApplicationRequest.isAsyncStarted() && !webApplicationRequest.isUpgraded()) { throw new IllegalStateException("Read listener cannot be set as the request is not upgraded nor the async is started"); } this.readListener = readListener; Thread thread = new Thread(this); thread.start(); } /** * Set the web application request. * * @param webApplicationRequest the web application request. */ public void setWebApplicationRequest(WebApplicationRequest webApplicationRequest) { this.webApplicationRequest = webApplicationRequest; } } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/WebApplicationManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import cloud.piranha.resource.api.ResourceManager; /** * The WebApplicationManager API. * *

* This API makes it possible to access the various managers used to deliver * specific functionality (e.g mime-type handling, welcome-file handling, etc). *

* * @author Manfred Riem (mriem@manorrock.com) */ public interface WebApplicationManager { /** * Get the annotation manager. * * @return the annotation manager. */ AnnotationManager getAnnotationManager(); /** * Get the async manager. * * @return the async manager. */ AsyncManager getAsyncManager(); /** * Get the error page manager. * * @return the error page manager. */ ErrorPageManager getErrorPageManager(); /** * Get the dispatcher manager. * * @return the dispatcher manager. */ DispatcherManager getDispatcherManager(); /** * Get the HandlesTypes manager. * * @return the HandlesTypes manager. */ HandlesTypesManager getHandlesTypesManager(); /** * Get the HTTP session manager. * * @return the HTTP session manager. */ HttpSessionManager getHttpSessionManager(); /** * Get the JSP manager. * * @return the JSP manager. */ JspManager getJspManager(); /** * Get the locale encoding manager. * * @return the locale encoding manager. */ LocaleEncodingManager getLocaleEncodingManager(); /** * Get the multi-part manager. * * @return the multi-part manager. */ MultiPartManager getMultiPartManager(); /** * Get the object instance manager. * * @return the object instance manager. */ ObjectInstanceManager getObjectInstanceManager(); /** * Get the resource manager. * * @return the resource manager. */ ResourceManager getResourceManager(); /** * Get the security manager. * * @return the security manager. */ SecurityManager getSecurityManager(); /** * Get the servlet request manager. * * @return the servlet request manager. */ ServletRequestManager getServletRequestManager(); /** * Get the welcome file manager. * * @return the welcome file manager. */ WelcomeFileManager getWelcomeFileManager(); /** * Set the annotation manager. * * @param annotationManager the annotation manager. */ void setAnnotationManager(AnnotationManager annotationManager); /** * Set the async manager. * * @param asyncManager the async manager. */ void setAsyncManager(AsyncManager asyncManager); /** * Set the error page manager. * * @param errorPageManager the error page manager. */ void setErrorPageManager(ErrorPageManager errorPageManager); /** * Set the HandlesTypes manager. * * @param handlesTypesManager the HandlesTypes manager. */ void setHandlesTypesManager(HandlesTypesManager handlesTypesManager); /** * Set the HTTP session manager. * * @param httpSessionManager the HTTP session manager. */ void setHttpSessionManager(HttpSessionManager httpSessionManager); /** * Set the JSP manager. * * @param jspManager the JSP manager. */ void setJspManager(JspManager jspManager); /** * Set the locale encoding manager. * * @param localeEncodingManager the locale encoding manager. */ void setLocaleEncodingManager(LocaleEncodingManager localeEncodingManager); /** * Set the multi-part manager. * * @param multiPartManager the multi-part manager. */ void setMultiPartManager(MultiPartManager multiPartManager); /** * Set the object instance manager. * * @param objectInstanceManager the object instance manager. */ void setObjectInstanceManager(ObjectInstanceManager objectInstanceManager); /** * Set the resource manager. * * @param resourceManager the resource manager. */ void setResourceManager(ResourceManager resourceManager); /** * Set the security manager. * * @param securityManager the security manager. */ void setSecurityManager(SecurityManager securityManager); /** * Set the servlet request manager. * * @param servletRequestManager the servlet request manager. */ void setServletRequestManager(ServletRequestManager servletRequestManager); /** * Set the welcome file manager. * * @param welcomeFileManager the welcome file manager. */ void setWelcomeFileManager(WelcomeFileManager welcomeFileManager); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/WebApplicationOutputStream.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.WriteListener; import java.io.IOException; import java.io.OutputStream; /** * The WebApplicationOutputStream API. * * @author Manfred Riem (mriem@manorrock.com) */ public abstract class WebApplicationOutputStream extends ServletOutputStream { /** * Default constructor. */ protected WebApplicationOutputStream() { } /** * Flush the buffer. * * @throws IOException when an I/O error occurs. */ public abstract void flushBuffer() throws IOException; /** * Get the buffer size. * * @return the buffer size. */ public abstract int getBufferSize(); /** * Get the output stream. * * @return the output stream. */ public abstract OutputStream getOutputStream(); /** * Get the web application response. * * @return the web application response. */ public abstract WebApplicationResponse getResponse(); /** * Get the write listener. * * @return the write listener. */ public abstract WriteListener getWriteListener(); /** * Reset the buffer. */ public abstract void resetBuffer(); /** * Set the buffer size. * * @param bufferSize the buffer size. */ public abstract void setBufferSize(int bufferSize); /** * Set the output stream. * * @param outputStream the output stream. */ public abstract void setOutputStream(OutputStream outputStream); /** * Set the web application response. * * @param response the web application response. */ public abstract void setResponse(WebApplicationResponse response); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/WebApplicationPrintWriter.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import java.io.PrintWriter; import java.io.Writer; /** * The WebApplicationPrintWriter API. * * @author Manfred Riem (mriem@manorrock.com) */ public class WebApplicationPrintWriter extends PrintWriter { /** * Stores the response object. */ protected WebApplicationResponse response; /** * Constructor. * * @param response the response object. * @param writer the writer. * @param autoFlush the auto flush flag. */ public WebApplicationPrintWriter(WebApplicationResponse response, Writer writer, boolean autoFlush) { super(writer, autoFlush); this.response = response; } } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/WebApplicationRequest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import jakarta.servlet.DispatcherType; import jakarta.servlet.MultipartConfigElement; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpUpgradeHandler; import java.security.Principal; import java.util.Map; /** * The WebApplicationRequest API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface WebApplicationRequest extends HttpServletRequest { /** * Get the modifiable parameter map. * * @return the modifiable parameter map. */ Map getModifiableParameterMap(); /** * {@return the multipartConfig} */ default MultipartConfigElement getMultipartConfig() { return null; } /** * {@return the upgrade handler} */ default HttpUpgradeHandler getUpgradeHandler() { return null; } /** * Get the web application input stream. * * @return the web application input stream. */ default WebApplicationInputStream getWebApplicationInputStream() { return null; } /** * {@return true when upgraded, false otherwise} */ default boolean isUpgraded() { return false; } /** * Set the async supported flag. * * @param asyncSupported the async supported flag. */ void setAsyncSupported(boolean asyncSupported); /** * Set the auth type. * * @param authType the auth type. */ void setAuthType(String authType); /** * Set the context path. * * @param contextPath the context path. */ void setContextPath(String contextPath); /** * Set the dispatcher type. * * @param dispatcherType the dispatcher type. */ void setDispatcherType(DispatcherType dispatcherType); /** * Set the requested session id. * * @param requestedSessionId the requested session id. */ default void setRequestedSessionId(String requestedSessionId) { } /** * Set the servlet path. * * @param servletPath the servlet path. */ void setServletPath(String servletPath); /** * Set the user principal. * * @param userPrincipal the user principal. */ void setUserPrincipal(Principal userPrincipal); /** * Set the web application. * * @param webApplication the web application. */ void setWebApplication(WebApplication webApplication); /** * Set the path info. * * @param pathInfo the path info. */ default void setPathInfo(String pathInfo) { } /** * Set the query string. * * @param queryString the query string. */ default void setQueryString(String queryString) { } /** * Set the web application input stream. * * @param webApplicationInputStream the web application input stream. */ default void setWebApplicationInputStream( WebApplicationInputStream webApplicationInputStream) { } } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/WebApplicationRequestMapper.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import jakarta.servlet.DispatcherType; import static jakarta.servlet.DispatcherType.REQUEST; import java.util.Collection; import java.util.Set; /** * The WebApplicationRequestMapper API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface WebApplicationRequestMapper { /** * Add a servlet mapping. * *

* This method will throw an IllegalArgumentException if a URL pattern * is null or empty (Servlet:JAVADOC:696.3). *

*

* This method accommodates for a URL pattern that does not start with a * leading slash to support pre-Servlet 2.3 application to operate * properly. *

* * @param servletName the servlet name. * @param urlPatterns the URL patterns to map (aka mappings). * @return the URL patterns that were already added. */ Set addServletMapping(String servletName, String... urlPatterns); /** * Remove a mapping. * * @param urlPattern the URL pattern * @return the Servlet name the pattern was mapped to, or null if no prior mapping. */ String removeServletMapping(String urlPattern); /** * Add a filter mapping. * *

* This adds the filter mappings at the end of list of existing mappings (if any). * * @param filterName the filter name. * @param urlPatterns the URL patterns to map (aka mappings). * @return the URL patterns that were added. */ default Set addFilterMapping(String filterName, String... urlPatterns) { return addFilterMapping(null, filterName, urlPatterns); } /** * Add a filter mapping. * *

* This adds the filter mappings at the end of list of existing mappings (if any). * * @param dispatcherTypes the dispatcher types. * @param filterName the filter name. * @param urlPatterns the URL patterns to map (aka mappings). * @return the URL patterns that were added. */ Set addFilterMapping(Set dispatcherTypes, String filterName, String... urlPatterns); /** * Add a filter mapping. * *

* This adds the filter mappings at the start of list of existing mappings (if any). * If there are existing mappings these are shifted to the right. * * @param filterName the filter name. * @param urlPatterns the URL patterns to map (aka mappings). * @return the URL patterns that were added. */ default Set addFilterMappingBeforeExisting(String filterName, String... urlPatterns) { return addFilterMappingBeforeExisting(null, filterName, urlPatterns); } /** * Add a filter mapping. * *

* This adds the filter mappings at the start of list of existing mappings (if any). * If there are existing mappings these are shifted to the right. * * @param dispatcherTypes the dispatcher types. * @param filterName the filter name. * @param urlPatterns the URL patterns to map (aka mappings). * @return the URL patterns that were added. */ Set addFilterMappingBeforeExisting(Set dispatcherTypes, String filterName, String... urlPatterns); /** * Find the filter mappings for the given path. * * @param path the path. * @return the mappings. */ default Collection findFilterMappings(String path) { return findFilterMappings(REQUEST, path); } /** * Find the filter mappings for the given path. * * @param dispatcherType the dispatcher type. * @param path the path. * @return the mappings. */ Collection findFilterMappings(DispatcherType dispatcherType, String path); /** * Find the servlet mapping for the given path. * * @param path the path. * @return the mapping, or null if not found. */ WebApplicationRequestMapping findServletMapping(String path); /** * Get the mappings for the specified servlet. * * @param servletName the servlet name. * @return the servlet mappings, or an empty collection if none. */ Collection getServletMappings(String servletName); /** * Get the default servlet. * * @return the default servlet. */ default String getDefaultServlet() { return null; } /** * Get the servlet name for the specified mapping. * * @param mapping the mapping. * @return the servlet name, or null if not found. */ String getServletName(String mapping); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/WebApplicationRequestMapping.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; /** * The WebApplicationRequestMapping API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface WebApplicationRequestMapping { /** * {@return the match value} */ String getMatchValue(); /** * {@return the path} */ String getPattern(); /** * Is this an exact match. * * @return true it it is, false otherwise. */ boolean isExact(); /** * Is this an extension match. * * @return true if it is, false otherwise. */ boolean isExtension(); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/WebApplicationResponse.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletResponse; import java.util.Collection; /** * The WebApplicationResponse API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface WebApplicationResponse extends HttpServletResponse { /** * Close the async response. */ void closeAsyncResponse(); /** * Get the content language. * * @return the content language. */ String getContentLanguage(); /** * Get the content length. * * @return the content length. */ long getContentLength(); /** * {@return the cookies} */ Collection getCookies(); /** * {@return the response closer} */ Runnable getResponseCloser(); /** * Get the status message. * * @return the status message. */ String getStatusMessage(); /** * Get the web application. * * @return the web application. */ WebApplication getWebApplication(); /** * {@return the web application output stream} */ WebApplicationOutputStream getWebApplicationOutputStream(); /** * Are we in body only mode. * * @return true if we are only sending the body, false otherwise. */ boolean isBodyOnly(); /** * Is the buffer resetting. * * @return true if it is, false otherwise. */ boolean isBufferResetting(); /** * Is the writer acquired. * * @return true if it is, false otherwise. */ boolean isWriterAcquired(); /** * Set the committed flag. * * @param committed the committed flag. */ void setCommitted(boolean committed); /** * Set the body only mode. * * @param bodyOnly the body only mode. */ void setBodyOnly(boolean bodyOnly); /** * Set the response closer. * * @param responseCloser the response closer. */ void setResponseCloser(Runnable responseCloser); /** * Set the web application. * * @param webApplication the web application. */ void setWebApplication(WebApplication webApplication); /** * Set the web application output stream. * * @param outputStream the web application output stream. */ void setWebApplicationOutputStream(WebApplicationOutputStream outputStream); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/WebApplicationServer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import jakarta.servlet.ServletException; import java.io.IOException; /** * The WebApplicationServer API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface WebApplicationServer { /** * Add a web application. * * @param webApplication the web application to add. */ void addWebApplication(WebApplication webApplication); /** * {@return the request mapper} */ WebApplicationServerRequestMapper getRequestMapper(); /** * Service the request and response. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. * @throws ServletException when a Servlet error occurs. */ void service(WebApplicationRequest request, WebApplicationResponse response) throws IOException, ServletException; /** * Initialize the server. */ void initialize(); /** * Set the request mapper. * * @param requestMapper the request mapper. */ void setRequestMapper(WebApplicationServerRequestMapper requestMapper); /** * Start the server. */ void start(); /** * Stop the server. */ void stop(); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/WebApplicationServerRequestMapper.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import java.util.Set; /** * The WebApplicationServerRequestMapper API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface WebApplicationServerRequestMapper { /** * Add a mapping. * * @param webApplication the web application. * @param urlPatterns the url patterns to map (aka mappings). * @return the url patterns added. */ Set addMapping(WebApplication webApplication, String... urlPatterns); /** * Find a mapping for the given path. * * @param path the path. * @return the mapping, or null if not found. */ WebApplication findMapping(String path); } ================================================ FILE: core/api/src/main/java/cloud/piranha/core/api/WelcomeFileManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import java.util.List; /** * The WelcomeFileManager API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface WelcomeFileManager { /** * Add a welcome file. * * @param welcomeFile the welcome file. */ void addWelcomeFile(String welcomeFile); /** * {@return the welcome file list} */ List getWelcomeFileList(); } ================================================ FILE: core/api/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module defines the core Piranha APIs. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.core.api { exports cloud.piranha.core.api; opens cloud.piranha.core.api; requires transitive cloud.piranha.resource.api; requires transitive jakarta.servlet; } ================================================ FILE: core/api/src/test/java/cloud/piranha/core/api/AttributeManagerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.api; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Collections; import java.util.Enumeration; import java.util.Hashtable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class AttributeManagerTest { private AttributeManager attributeManager; private Hashtable attributes; @BeforeEach void setUp() { attributes = new Hashtable<>(); attributeManager = new AttributeManager() { @Override public Object getAttribute(String name) { return attributes.get(name); } @Override public Enumeration getAttributeNames() { return attributes.keys(); } @Override public void removeAttribute(String name) { attributes.remove(name); } @Override public void setAttribute(String name, Object value) { attributes.put(name, value); } }; } @Test void testGetAttribute() { attributeManager.setAttribute("test", "value"); assertEquals("value", attributeManager.getAttribute("test")); } @Test void testGetAttributeNames() { attributeManager.setAttribute("test1", "value1"); attributeManager.setAttribute("test2", "value2"); Enumeration names = attributeManager.getAttributeNames(); assertTrue(Collections.list(names).contains("test1")); names = attributeManager.getAttributeNames(); assertTrue(Collections.list(names).contains("test2")); } @Test void testRemoveAttribute() { attributeManager.setAttribute("test", "value"); attributeManager.removeAttribute("test"); assertNull(attributeManager.getAttribute("test")); } @Test void testSetAttribute() { attributeManager.setAttribute("test", "value"); assertEquals("value", attributeManager.getAttribute("test")); } @Test void testContainsAttribute() { attributeManager.setAttribute("test", "value"); assertTrue(attributeManager.containsAttribute("test")); attributeManager.removeAttribute("test"); assertFalse(attributeManager.containsAttribute("test")); } } ================================================ FILE: core/impl/pom.xml ================================================ 4.0.0 cloud.piranha.core project 25.4.0-SNAPSHOT piranha-core-impl jar Piranha - Core - Implementation org.jacoco jacoco-maven-plugin check check BUNDLE INSTRUCTION COVEREDRATIO 0.69 BRANCH COVEREDRATIO 0.57 cloud.piranha bom ${project.version} pom import cloud.piranha.core piranha-core-api ${project.version} compile cloud.piranha.resource piranha-resource-impl ${project.version} compile cloud.piranha.http piranha-http-impl ${project.version} test org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test default file:///tmp/piranha/core/impl/ ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/AsyncHttpDispatchWrapper.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import java.security.Principal; import java.util.ArrayList; import static java.util.Collections.enumeration; import static java.util.Collections.list; import static java.util.Collections.unmodifiableMap; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import static java.util.Objects.requireNonNull; import cloud.piranha.core.api.AttributeManager; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationRequest; import jakarta.servlet.AsyncContext; import jakarta.servlet.DispatcherType; import static jakarta.servlet.DispatcherType.ASYNC; import jakarta.servlet.MultipartConfigElement; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequestWrapper; /** * The async HTTP dispatch wrapper. * * @author Manfred Riem (mriem@manorrock.com) */ public class AsyncHttpDispatchWrapper extends HttpServletRequestWrapper implements WebApplicationRequest { /** * Stores the servlet path. */ private String servletPath; /** * Stores the path info. */ private String pathInfo; /** * Stores the request URI. */ private String requestURI; /** * Stores the query string. */ private String queryString; /** * Stores the async context. */ private AsyncContext asyncContext; /** * Stores the async started flag. */ private boolean asyncStarted; // Note that asyncStarted is per async cycle, and resets when the request is dispatched /** * Stores the attribute manager. */ private AttributeManager attributeManager = new DefaultAttributeManager(); /** * Stores the wrapper attributes. */ private List wrapperAttributes = new ArrayList<>(); /** * Stores the wrapper parameters. */ private Map wrapperParameters = new HashMap<>(); /** * Constructor. * * @param request the HTTP servlet request. */ public AsyncHttpDispatchWrapper(HttpServletRequest request) { super(request); wrapperAttributes.add("piranha.response"); } @Override public Map getModifiableParameterMap() { return wrapperParameters; } @Override public HttpServletRequest getRequest() { return (HttpServletRequest) super.getRequest(); } @Override public DispatcherType getDispatcherType() { return ASYNC; } @Override public String getServletPath() { return servletPath; } @Override public String getPathInfo() { return pathInfo; } @Override public void setPathInfo(String pathInfo) { this.pathInfo = pathInfo; } @Override public String getRequestURI() { return requestURI; } /** * Set the request URI. * * @param requestURI the request URI. */ public void setRequestURI(String requestURI) { this.requestURI = requestURI; } @Override public String getQueryString() { return queryString; } @Override public AsyncContext startAsync() throws IllegalStateException { return startAsync(this, requireNonNull((ServletResponse) getAttribute("piranha.response"))); } @Override public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { if (asyncContext != null) { throw new IllegalStateException("Async cycle has already been started"); } servletRequest.setAttribute("CALLED_FROM_ASYNC_WRAPPER", "true"); asyncContext = super.startAsync(servletRequest, servletResponse); asyncStarted = true; return asyncContext; } @Override public boolean isAsyncStarted() { return asyncStarted; } @Override public Enumeration getAttributeNames() { HashSet attributeNames = new HashSet<>(); attributeNames.addAll(list(super.getAttributeNames())); attributeNames.addAll(wrapperAttributes); return enumeration(attributeNames); } @Override public Object getAttribute(String name) { if (wrapperAttributes.contains(name)) { return attributeManager.getAttribute(name); } return super.getAttribute(name); } @Override public void setAttribute(String name, Object object) { if (wrapperAttributes.contains(name)) { attributeManager.setAttribute(name, object); } else { super.setAttribute(name, object); } } @Override public void removeAttribute(String name) { if (wrapperAttributes.contains(name)) { attributeManager.removeAttribute(name); } else { super.removeAttribute(name); } } /** * Get the parameter. * * @param name the name. * @return the value. */ @Override public String getParameter(String name) { if (getParameterValues(name) == null) { return null; } return getParameterValues(name)[0]; } /** * {@return the parameter map} */ @Override public Map getParameterMap() { Map parameterMap = new HashMap<>(); parameterMap.putAll(super.getParameterMap()); parameterMap.putAll(wrapperParameters); return unmodifiableMap(parameterMap); } /** * {@return the parameter names} */ @Override public Enumeration getParameterNames() { HashSet parameterNames = new HashSet<>(); parameterNames.addAll(super.getParameterMap().keySet()); parameterNames.addAll(wrapperParameters.keySet()); return enumeration(parameterNames); } /** * {@return the parameter values} * @param name the parameter name. */ @Override public String[] getParameterValues(String name) { if (wrapperParameters.containsKey(name)) { return wrapperParameters.get(name); } return super.getParameterValues(name); } @Override public void setQueryString(String queryString) { this.queryString = queryString; } /** * Set as a wrapper attribute. * * @param name the name. * @param value the value. */ public void setAsWrapperAttribute(String name, Object value) { attributeManager.setAttribute(name, value); } /** * {@return the wrapper attributes} */ public List getWrapperAttributes() { return wrapperAttributes; } /** * {@return the wrapper parameters} */ public Map getWrapperParameters() { return wrapperParameters; } @Override public void setDispatcherType(DispatcherType dispatcherType) { // cannot change the dispatcher type. } @Override public String toString() { return getRequestURIWithQueryString() + " " + super.toString(); } /** * {@return the request URI with query string} */ public String getRequestURIWithQueryString() { StringBuilder builder = new StringBuilder(); builder.append(getRequestURI()); if (getQueryString() != null) { builder.append("?").append(queryString); } return builder.toString(); } @Override public void setContextPath(String contextPath) { // cannot change the context path. } @Override public void setServletPath(String servletPath) { this.servletPath = servletPath; } @Override public void setAuthType(String authType) { // cannot be set. } @Override public void setUserPrincipal(Principal userPrincipal) { // cannot be set. } @Override public void setWebApplication(WebApplication webApplication) { // cannot be set. } @Override public MultipartConfigElement getMultipartConfig() { return null; } @Override public void setAsyncSupported(boolean asyncSupported) { } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/AsyncNonHttpDispatchWrapper.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import jakarta.servlet.DispatcherType; import static jakarta.servlet.DispatcherType.ASYNC; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletRequestWrapper; /** * The async non-HTTP dispatch wrapper. * * @author Manfred Riem (mriem@manorrock.com) */ public class AsyncNonHttpDispatchWrapper extends ServletRequestWrapper { /** * Constructor. * * @param request the servlet request. */ public AsyncNonHttpDispatchWrapper(ServletRequest request) { super(request); } @Override public DispatcherType getDispatcherType() { return ASYNC; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/CookieParser.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import java.util.ArrayList; import java.util.List; import java.util.Objects; import jakarta.servlet.http.Cookie; /** * The Cookie parser */ public class CookieParser { /** * Stores the $Version= constant */ private static final String VERSION = "$Version="; private CookieParser() { } /** * Parse the Cookie header * * @param cookieValues the Cookie header, without "Cookie:" * @return the {@link Cookie} array * @throws NullPointerException if cookieValues is null * @throws IllegalArgumentException if some cookie contains a illegal * character */ public static Cookie[] parse(String cookieValues) { Objects.requireNonNull(cookieValues); if (cookieValues.startsWith(VERSION)) { return parseRFC2109(cookieValues.substring(cookieValues.indexOf(';') + 1)); } return parseNetscape(cookieValues); } private static Cookie[] parseNetscape(String cookiesValue) { ArrayList cookieList = new ArrayList<>(); String[] cookieCandidates = cookiesValue.split(";"); for (String cookieCandidate : cookieCandidates) { String[] cookieString = cookieCandidate.split("="); String cookieName = cookieString[0].trim(); String cookieValue = null; if (cookieString.length == 2) { cookieValue = cookieString[1].trim(); } Cookie cookie = new Cookie(cookieName, cookieValue); cookieList.add(cookie); } return cookieList.toArray(Cookie[]::new); } @SuppressWarnings({"removal"}) private static Cookie[] parseRFC2109(String cookiesValue) { List cookieList = new ArrayList<>(); String[] cookieCandidates = cookiesValue.split("[;,]"); Cookie currentCookie = null; for (String cookieCandidate : cookieCandidates) { String[] values = cookieCandidate.trim().split("=", 2); String name = removeQuotes(values[0].trim()); String value = values.length == 2 ? removeQuotes(values[1].trim()) : null; if (name.startsWith("$")) { if (currentCookie == null) { throw new IllegalArgumentException("Invalid Cookie"); } if ("$Domain".equals(name)) { currentCookie.setDomain(value); cookieList.add(new Cookie(name, value)); } else if ("$Path".equals(name)) { currentCookie.setPath(value); cookieList.add(new Cookie(name, value)); } } else { currentCookie = new Cookie(name, value); currentCookie.setVersion(1); cookieList.add(currentCookie); } } return cookieList.toArray(Cookie[]::new); } private static String removeQuotes(String value) { Objects.requireNonNull(value); if (value.startsWith("\"") && value.endsWith("\"")) { return value.substring(1, value.length() - 1); } return value; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultAnnotationManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.AnnotationInfo; import cloud.piranha.core.api.AnnotationManager; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; /** * The default AnnotationManager. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultAnnotationManager implements AnnotationManager { /** * Stores the annotated classes. */ private HashMap, Set>> annotatedClasses = new HashMap<>(); /** * Constructor. */ public DefaultAnnotationManager() { } @Override public void addAnnotatedClass(Class annotationClass, Class clazz) { if (annotationClass == null) { throw new IllegalArgumentException("annotationClass cannot be null"); } if (!annotatedClasses.containsKey(annotationClass)) { HashSet> classes = new HashSet<>(); classes.add(clazz); annotatedClasses.put(annotationClass, classes); } else { @SuppressWarnings({ "rawtypes", "unchecked" }) HashSet> classes = (HashSet) annotatedClasses.get(annotationClass); classes.add(clazz); } } @Override public void addAnnotation(AnnotationInfo annotationInfo) { if (annotationInfo == null) { throw new IllegalArgumentException("annotationInfo cannot be null"); } } @Override public void addInstance(Class instanceClass, Class implementingClass) { if (instanceClass == null) { throw new IllegalArgumentException("instanceClass cannot be null"); } } @Override public List> getAnnotations(Class annotationClass) { return Collections.emptyList(); } @Override public List> getAnnotations(Class... annotationClasses) { return Collections.emptyList(); } @Override public List> getInstances(Class instanceClass) { return Collections.emptyList(); } @Override public List> getInstances(Class... instanceClasses) { return Collections.emptyList(); } @Override public List> getAnnotationsByTarget(Class annotationClass, AnnotatedElement type) { return Collections.emptyList(); } @Override public Set> getAnnotatedClass(Class annotationClass) { return annotatedClasses.getOrDefault(annotationClass, Collections.emptySet()); } @SuppressWarnings("unchecked") @Override public Set> getAnnotatedClasses(Class[] annotationClasses) { HashSet> result = new HashSet<>(); if (annotationClasses != null) { for (Class annotationClass : annotationClasses) { result.addAll(getAnnotatedClass((Class) annotationClass)); } } return result; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultAsyncContext.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.AsyncManager; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationRequest; import cloud.piranha.core.api.WebApplicationResponse; import jakarta.servlet.AsyncContext; import jakarta.servlet.AsyncEvent; import jakarta.servlet.AsyncListener; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletRequestWrapper; import jakarta.servlet.ServletResponse; import jakarta.servlet.ServletResponseWrapper; import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; import java.lang.System.Logger; import static java.lang.System.Logger.Level.DEBUG; import static java.lang.System.Logger.Level.WARNING; import java.util.ArrayList; import java.util.List; import static java.util.Objects.requireNonNull; import java.util.concurrent.ScheduledThreadPoolExecutor; import static java.util.concurrent.TimeUnit.MILLISECONDS; /** * The default AsyncContext. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultAsyncContext implements AsyncContext { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(DefaultAsyncContext.class.getName()); /** * Stores the listeners. */ private final List listeners = new ArrayList<>(); /** * The request that comes from a call to request.startAsync() * *

* This is either the request the caller passed in when using * request.startAsync(someRequest, someResponse) or it's the * request object on which startAsync was called when using the * zero argument version request.startAsync(). * *

* In the latter case, the request is guaranteed to be the "original * request", which is the request passed to the servlet that started the * async cycle. In the former case it can either be the "original request", * or it can be that request but wrapped by user code. */ private final ServletRequest asyncStartRequest; /** * The response that comes from a call to request.startAsync() * *

* This is either the response the caller passed in when using * request.startAsync(someRequest, someResponse) or it's the * response object associated with the request object on which * startAsync was called when using the zero argument version * request.startAsync(). * *

* In the latter case, the response is guaranteed to be the "original * response", which is the response passed to the servlet that started the * async cycle. In the former case it can either be the "original response", * or it can be that response but wrapped by user code. */ private final ServletResponse asyncStartResponse; /** * "the request object passed to the first servlet object in the call chain * that received the request from the client." */ private final WebApplicationRequest originalRequest; /** * The response object passed to the first servlet object in the call chain * that received the request from the client." */ private final WebApplicationResponse originalResponse; /** * Stores the timeout. */ private long timeout = Long.parseLong(System.getProperty("piranha.async.timeout", "30000")); // 30 seconds, as mandated by spec /** * Tracks whether dispatch() has already been called on this context or not. */ private boolean dispatched; /** * Stores the scheduled thread pool executor. */ private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1); /** * Constructor. * * @param asyncStartRequest the servlet asyncStartRequest. * @param asyncStartResponse the servlet asyncStartResponse. */ public DefaultAsyncContext(ServletRequest asyncStartRequest, ServletResponse asyncStartResponse) { this.asyncStartRequest = requireNonNull(asyncStartRequest); this.asyncStartResponse = requireNonNull(asyncStartResponse); originalRequest = unwrapFully(asyncStartRequest); originalResponse = unwrapFully(asyncStartResponse); scheduledThreadPoolExecutor.schedule(this::onTimeOut, timeout, MILLISECONDS); } @Override public void addListener(AsyncListener listener) { this.listeners.add(listener); } @Override public void addListener(AsyncListener listener, ServletRequest request, ServletResponse response) { this.listeners.add(listener); } @Override public T createListener(Class type) throws ServletException { try { return type.getConstructor().newInstance(); } catch (Throwable t) { LOGGER.log(WARNING, () -> "Unable to create AsyncListener: " + type.getName(), t); throw new ServletException("Unable to create listener", t); } } /** * @see AsyncContext#dispatch() */ @Override public void dispatch() { String path; if (asyncStartRequest instanceof HttpServletRequest httpServletRequest) { path = httpServletRequest.getRequestURI().substring(httpServletRequest.getContextPath().length()); } else { path = originalRequest.getRequestURI().substring(originalRequest.getContextPath().length()); } dispatch(path); } /** * @see AsyncContext#dispatch(java.lang.String) */ @Override public void dispatch(String path) { dispatch(asyncStartRequest.getServletContext(), path); } /** * Dispatch. * * @param servletContext the servlet context. * @param path the path. */ @Override public void dispatch(ServletContext servletContext, String path) { if (dispatched) { throw new IllegalStateException("Dispatch already called on this async contexct"); } dispatched = true; WebApplication webApplication = (WebApplication) servletContext; AsyncManager asyncManager = webApplication.getManager().getAsyncManager(); asyncManager.getDispatcher(webApplication, path, asyncStartRequest, asyncStartResponse) .dispatch(); } @Override public void complete() { scheduledThreadPoolExecutor.shutdownNow(); LOGGER.log(DEBUG, () -> "Completing async processing"); if (!listeners.isEmpty()) { listeners.forEach(listener -> { try { listener.onComplete(new AsyncEvent(this)); } catch (IOException ioe) { LOGGER.log(WARNING, () -> "IOException when calling onComplete on AsyncListener", ioe); } }); } LOGGER.log(DEBUG, () -> "Flushing async asyncStartResponse buffer"); try { asyncStartResponse.flushBuffer(); } catch (IOException ioe) { LOGGER.log(WARNING, () -> "IOException when flushing async asyncStartResponse buffer", ioe); } originalResponse.closeAsyncResponse(); } /** * Process on timeout */ public void onTimeOut() { scheduledThreadPoolExecutor.shutdownNow(); if (!listeners.isEmpty()) { listeners.forEach(listener -> { try { listener.onTimeout(new AsyncEvent(this)); } catch (IOException ioe) { LOGGER.log(WARNING, () -> "IOException when calling onTimeout on AsyncListener", ioe); } }); } LOGGER.log(DEBUG, () -> "Flushing async asyncStartResponse buffer"); if (!asyncStartResponse.isCommitted()) { try { asyncStartResponse.flushBuffer(); } catch (IOException ioe) { LOGGER.log(WARNING, () -> "IOException when flushing async asyncStartResponse buffer", ioe); } } originalResponse.closeAsyncResponse(); } @Override public ServletRequest getRequest() { return asyncStartRequest; } @Override public ServletResponse getResponse() { return asyncStartResponse; } @Override public long getTimeout() { return timeout; } @Override public boolean hasOriginalRequestAndResponse() { return originalRequest == asyncStartRequest && originalResponse == asyncStartResponse; } @Override public void setTimeout(long timeout) { this.timeout = timeout; } /** * Start the thread. * * @param runnable the runnable. */ @Override public void start(Runnable runnable) { LOGGER.log(DEBUG, "Starting async context with: {0}", runnable); Thread thread = new Thread(runnable); thread.start(); } /** * Unwrap the servlet request. * * @param the type. * @param request the request to unwrap. * @return the unwrapped request. */ @SuppressWarnings("unchecked") private T unwrapFully(ServletRequest request) { ServletRequest currentRequest = request; while (currentRequest instanceof ServletRequestWrapper wrapper) { currentRequest = wrapper.getRequest(); } return (T) currentRequest; } /** * Unwrap the servlet response. * * @param the type. * @param response the response to unwrap. * @return the unwrapped response. */ @SuppressWarnings("unchecked") private T unwrapFully(ServletResponse response) { ServletResponse currentResponse = response; while (currentResponse instanceof ServletResponseWrapper wrapper) { currentResponse = wrapper.getResponse(); } return (T) currentResponse; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultAsyncDispatcher.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.AsyncDispatcher; import cloud.piranha.core.api.WebApplication; import jakarta.servlet.AsyncContext; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.http.HttpServletRequest; import java.lang.System.Logger; import static java.lang.System.Logger.Level.WARNING; /** * The default AsyncDispatcher. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultAsyncDispatcher implements AsyncDispatcher { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(DefaultAsyncDispatcher.class.getName()); /** * Stores the web application. */ private final WebApplication webApplication; /** * Stores the path. */ private final String path; /** * Stores the async start request. */ private final ServletRequest asyncStartRequest; /** * Stores the async start response. */ private final ServletResponse asyncStartResponse; /** * Constructor. * * @param webApplication the web application. * @param path the path. * @param asyncStartRequest the request. * @param asyncStartResponse the asyncStartResponse. */ public DefaultAsyncDispatcher(WebApplication webApplication, String path, ServletRequest asyncStartRequest, ServletResponse asyncStartResponse) { this.webApplication = webApplication; this.path = path; this.asyncStartRequest = asyncStartRequest; this.asyncStartResponse = asyncStartResponse; } @Override public void dispatch() { AsyncContext asyncContext = asyncStartRequest.getAsyncContext(); RequestDispatcher requestDispatcher = webApplication.getRequestDispatcher(path); new Thread(() -> { Thread.currentThread().setContextClassLoader(webApplication.getClassLoader()); ServletRequest dispatchedRequest = addAsyncWrapper(asyncStartRequest); try { requestDispatcher.forward(dispatchedRequest, asyncStartResponse); } catch (Throwable t) { LOGGER.log(WARNING, "Error occurred during dispatch", t); } if (!dispatchedRequest.isAsyncStarted()) { asyncContext.complete(); } }).start(); } private ServletRequest addAsyncWrapper(ServletRequest request) { if (request instanceof HttpServletRequest httpServletRequest) { return new AsyncHttpDispatchWrapper(httpServletRequest); } return new AsyncNonHttpDispatchWrapper(request); } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultAsyncManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.AsyncDispatcher; import cloud.piranha.core.api.AsyncManager; import cloud.piranha.core.api.WebApplication; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; /** * The default implementation of the AsyncManager API. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultAsyncManager implements AsyncManager { /** * Constructor. */ public DefaultAsyncManager() { } @Override public AsyncDispatcher getDispatcher(WebApplication webApplication, String path, ServletRequest asyncStartRequest, ServletResponse asyncStartResponse) { return new DefaultAsyncDispatcher(webApplication, path, asyncStartRequest, asyncStartResponse); } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultAttributeManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.AttributeManager; import java.util.Collections; import java.util.Enumeration; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * The default AttributeManager. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultAttributeManager implements AttributeManager { /** * Stores the attributes. */ protected Map attributes; /** * Constructor. */ public DefaultAttributeManager() { attributes = new ConcurrentHashMap<>(); } @Override public Object getAttribute(String name) { return attributes.get(name); } @Override public Enumeration getAttributeNames() { return Collections.enumeration(attributes.keySet()); } @Override public void removeAttribute(String name) { attributes.remove(name); } @Override public void setAttribute(String name, Object value) { if (value != null) { attributes.put(name, value); } else { attributes.remove(name); } } @Override public boolean containsAttribute(String name) { return attributes.containsKey(name); } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultAuthenticatedIdentity.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import static java.util.Collections.unmodifiableSet; import java.security.Principal; import java.util.HashSet; import java.util.Set; import javax.security.auth.Subject; import cloud.piranha.core.api.AuthenticatedIdentity; /** * Default implementation of AuthenticatedIdentity. * *

* This implementation is an immutable structure, with a facility to store it in * TLS. It's the responsibility of the context, e.g. the HTTP request handler, * to remove the identity from TLS at the end of the context (e.g. end of the * HTTP request), or at any other appropriate time (e.g. when logging out * mid-request). * * * @author Arjan Tijms * */ public class DefaultAuthenticatedIdentity implements AuthenticatedIdentity { /** * Stores the current identity. */ private static InheritableThreadLocal currentIdentity = new InheritableThreadLocal<>(); /** * Stores the current subject. */ private static InheritableThreadLocal currentSubject = new InheritableThreadLocal<>(); /** * Stores the caller principal. */ private Principal callerPrincipal; /** * Stores the groups. */ private Set groups = new HashSet<>(); /** * Constructor. * * @param callerPrincipal the caller principal. * @param groups the groups. */ public DefaultAuthenticatedIdentity(Principal callerPrincipal, Set groups) { this.callerPrincipal = callerPrincipal; this.groups = unmodifiableSet(groups); } /** * Set the current identity. * * @param callerPrincipal the caller principal. * @param groups the groups. */ public static void setCurrentIdentity(Principal callerPrincipal, Set groups) { setCurrentIdentity(new DefaultAuthenticatedIdentity(callerPrincipal, groups)); } /** * Set the current identity. * * @param identity the identity. */ public static void setCurrentIdentity(AuthenticatedIdentity identity) { Subject subject = new Subject(); subject.getPrincipals().add(identity); if (identity.getCallerPrincipal() != null) { subject.getPrincipals().add(identity.getCallerPrincipal()); } currentIdentity.set(identity); currentSubject.set(subject); } /** * {@return the current subject} */ public static Subject getCurrentSubject() { return currentSubject.get(); } /** * {@return the current identity} */ public static AuthenticatedIdentity getCurrentIdentity() { return currentIdentity.get(); } /** * Clear identity and subject. */ public static void clear() { currentIdentity.remove(); currentSubject.remove(); } @Override public Principal getCallerPrincipal() { return callerPrincipal; } @Override public Set getGroups() { return groups; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultCurrentRequestHolder.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.CurrentRequestHolder; import jakarta.servlet.http.HttpServletRequest; /** * Default implementation of the CurrentRequestHolder interface. * * @author Arjan Tijms * */ public class DefaultCurrentRequestHolder implements CurrentRequestHolder { /** * Stores the HTTP servlet request. */ private HttpServletRequest request; /** * Constructor. * * @param request the HTTP servlet request. */ public DefaultCurrentRequestHolder(HttpServletRequest request) { this.request = request; } @SuppressWarnings("unchecked") @Override public T getRequest() { return (T) request; } @Override public void setRequest(HttpServletRequest request) { this.request = request; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultDispatcherManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.DispatcherManager; import cloud.piranha.core.api.ServletEnvironment; import cloud.piranha.core.api.WebApplication; import jakarta.servlet.RequestDispatcher; /** * The default DispatcherManager. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultDispatcherManager implements DispatcherManager { /** * Stores the web application. */ private WebApplication webApplication; /** * Constructor. */ public DefaultDispatcherManager() { } @Override public RequestDispatcher getNamedDispatcher(String name) { RequestDispatcher dispatcher = null; ServletEnvironment environment = (ServletEnvironment) webApplication .getServletRegistration(name); if (environment != null) { dispatcher = new DefaultNamedRequestDispatcher(environment); } return dispatcher; } @Override public void setWebApplication(WebApplication webApplication) { this.webApplication = webApplication; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultErrorPageManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.ErrorPageManager; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.Map; /** * The default ErrorPageManager. * * @author Arjan Tijms * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultErrorPageManager implements ErrorPageManager { /** * Stores the error pages by code. */ private final Map errorPagesByCode = new HashMap<>(); /** * Stores the error pages by exception. */ private final Map errorPagesByException = new HashMap<>(); /** * Constructor. */ public DefaultErrorPageManager() { } @Override public void addErrorPage(int statusCode, String page) { errorPagesByCode.put(statusCode, page); } @Override public void addErrorPage(String throwableClassName, String page) { errorPagesByException.put(throwableClassName, page); } @Override public String getErrorPage(Throwable exception, HttpServletResponse httpResponse) { if (exception != null) { Class rootException = exception.getClass(); String page = null; while (rootException != null && page == null) { page = errorPagesByException.get(rootException.getName()); rootException = rootException.getSuperclass(); } if (page == null && exception instanceof ServletException servletException) { page = getErrorPage(servletException.getRootCause(), httpResponse); } return page; } if (httpResponse.getStatus() >= 400) { return errorPagesByCode.get(httpResponse.getStatus()); } // No error return null; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultFilterChain.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.ServletInvocation; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.Servlet; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.UnavailableException; import jakarta.servlet.http.HttpServletResponse; import static jakarta.servlet.http.HttpServletResponse.SC_NOT_FOUND; import java.io.IOException; import java.lang.System.Logger; import static java.lang.System.Logger.Level.TRACE; /** * The default FilterChain. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultFilterChain implements FilterChain { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(DefaultFilterChain.class.getName()); /** * Stores the filter. */ private Filter filter; /** * Stores the next filter chain. */ private FilterChain nextFilterChain; /** * Stores the servlet. */ private Servlet servlet; /** * Stores the servlet invocation. */ private ServletInvocation servletInvocation; /** * Constructor. */ public DefaultFilterChain() { } /** * Constructor. * * @param servletInvocation the servlet invocation. * @param servlet the servlet. */ public DefaultFilterChain(ServletInvocation servletInvocation, Servlet servlet) { this.servletInvocation = servletInvocation; this.servlet = servlet; } /** * Constructor. * * @param filter the filter. * @param nextFilterChain the next filter chain. */ public DefaultFilterChain(Filter filter, FilterChain nextFilterChain) { this.filter = filter; this.nextFilterChain = nextFilterChain; } /** * Process the request. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. * @throws ServletException when a servlet error occurs. */ @Override public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if (filter != null) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Calling filter: {0}", filter); } filter.doFilter(request, response, nextFilterChain); if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Called filter: {0}", filter); } } else if (servlet != null) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Calling servlet: {0}", servlet); } request.setAttribute(DefaultServletEnvironment.class.getName(), servlet.getServletConfig()); try { servlet.service(request, response); } finally { request.removeAttribute(DefaultServletEnvironment.class.getName()); } if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Called servlet: {0}", servlet); } } else if (servletInvocation != null && servletInvocation.isServletUnavailable()) { // We've reached the servlet, but the servlet is not available (for instance because // the init method failed) Exception exception; Throwable throwable = servletInvocation.getServletEnvironment().getUnavailableException(); if (throwable instanceof Exception e) { exception = e; } else { exception = new UnavailableException(""); exception.initCause(throwable); } if (response instanceof HttpServletResponse httpServletResponse) { httpServletResponse.setStatus(500); } if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "The servlet: {0} is unavailable, because of: {1}", servletInvocation.getServletName(), servletInvocation.getServletEnvironment().getUnavailableException()); } request.setAttribute("piranha.request.exception", exception); throw new ServletException(exception); } else if (response instanceof HttpServletResponse httpServletResponse) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "We did not find a proper filter chain for request: {0} and response: {1}", request, response); } httpServletResponse.sendError(SC_NOT_FOUND); } } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultFilterEnvironment.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.FilterEnvironment; import cloud.piranha.core.api.WebApplication; import jakarta.servlet.DispatcherType; import jakarta.servlet.Filter; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; /** * The default FilterEnvironment. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultFilterEnvironment implements FilterEnvironment { /** * Stores the async supported flag. */ private boolean asyncSupported; /** * Stores the class name. */ private String className; /** * Stores the filter. */ private Filter filter; /** * Stores the filter name. */ private String filterName; /** * Stores the init parameters. */ private HashMap initParameters; /** * Stores the servlet mame mappings. */ private ConcurrentHashMap servletNameMappings; /** * Stores the status. */ private int status; /** * Stores the url pattern mappings. */ private ConcurrentHashMap urlPatternMappings; /** * Stores the web application. */ private WebApplication webApplication; /** * Constructor. */ public DefaultFilterEnvironment() { initParameters = new HashMap<>(); servletNameMappings = new ConcurrentHashMap<>(); urlPatternMappings = new ConcurrentHashMap<>(); } /** * Constructor. * * @param webApp the web application. * @param filterName the filter name. * @param filter the filter. */ public DefaultFilterEnvironment(WebApplication webApp, String filterName, Filter filter) { this(); this.webApplication = webApp; this.filterName = filterName; this.filter = filter; this.className = filter.getClass().getName(); } @Override public void addMappingForServletNames(EnumSet dispatcherTypes, boolean isMatchAfter, String... servletNames) { String[] names = Stream.of(servletNames).map(s -> "servlet:// " + s).toArray(String[]::new); webApplication.addFilterMapping(dispatcherTypes, filterName, isMatchAfter, names); Arrays.stream(servletNames).forEach(x -> servletNameMappings.put(x, filterName)); } @Override public void addMappingForUrlPatterns(EnumSet dispatcherTypes, boolean isMatchAfter, String... urlPatterns) { webApplication.addFilterMapping(dispatcherTypes, filterName, isMatchAfter, urlPatterns); Arrays.stream(urlPatterns).forEach(x -> urlPatternMappings.put(x, filterName)); } @Override public String getClassName() { return className; } @Override public Filter getFilter() { return filter; } @Override public String getFilterName() { return filterName; } @Override public String getInitParameter(String name) { return initParameters.get(name); } @Override public Enumeration getInitParameterNames() { return Collections.enumeration(initParameters.keySet()); } @Override public Map getInitParameters() { return initParameters; } @Override public String getName() { return filterName; } @Override public ServletContext getServletContext() { return this.webApplication; } @Override public Collection getServletNameMappings() { return Collections.unmodifiableCollection(servletNameMappings.keySet()); } /** * Get the status. * * @return the status. */ public int getStatus() { return status; } @Override public Collection getUrlPatternMappings() { return Collections.unmodifiableCollection(urlPatternMappings.keySet()); } @Override public WebApplication getWebApplication() { return webApplication; } @Override public void initialize() throws ServletException { if (filter == null) { try { Class clazz = webApplication.getClassLoader().loadClass(className).asSubclass(Filter.class); filter = webApplication.createFilter(clazz); } catch (Throwable throwable) { throw new ServletException("Unable to initialize the filter", throwable); } } } @Override public boolean isAsyncSupported() { return asyncSupported; } @Override public void setAsyncSupported(boolean asyncSupported) { this.asyncSupported = asyncSupported; } @Override public void setClassName(String className) { this.className = className; } @Override public void setFilterName(String filterName) { this.filterName = filterName; } @Override public boolean setInitParameter(String name, String value) { boolean result = false; if (!initParameters.containsKey(name)) { initParameters.put(name, value); result = true; } return result; } @Override public Set setInitParameters(Map initParameters) { HashSet conflicting = new HashSet<>(); if (initParameters != null) { initParameters.entrySet().forEach(entry -> { String name = entry.getKey(); String value = entry.getValue(); if (name == null) { throw new IllegalArgumentException("A null name is not allowed"); } if (value == null) { throw new IllegalArgumentException("A null value is not allowed"); } if (!setInitParameter(name, value)) { conflicting.add(name); } }); } return conflicting; } @Override public void setStatus(int status) { this.status = status; } @Override public void setWebApplication(WebApplication webApplication) { this.webApplication = webApplication; } @Override public String toString() { return (className != null? className : "") + " " + (!urlPatternMappings.isEmpty()? urlPatternMappings : "") + (!servletNameMappings.isEmpty()? servletNameMappings : "") + " " + super.toString(); } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultFilterMapping.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.FilterMapping; import jakarta.servlet.DispatcherType; import static jakarta.servlet.DispatcherType.REQUEST; import java.util.Objects; /** * The default FilterMapping. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultFilterMapping implements FilterMapping { /** * Stores the dispatcher type. */ private final DispatcherType dispatcherType; /** * Stores the filter name. */ private final String filterName; /** * Stores the URL pattern. */ private final String urlPattern; /** * Constructor. * * @param filterName the filter name. * @param urlPattern the URL pattern. */ public DefaultFilterMapping(String filterName, String urlPattern) { this(REQUEST, filterName, urlPattern); } /** * Constructor. * * @param dispatcherType the dispatcher type. * @param filterName the filter name. * @param urlPattern the URL pattern. */ public DefaultFilterMapping(DispatcherType dispatcherType, String filterName, String urlPattern) { this.dispatcherType = dispatcherType; this.filterName = filterName; this.urlPattern = urlPattern; } @Override public boolean equals(Object object) { boolean result = false; if (object instanceof DefaultFilterMapping mapping && mapping.filterName.equals(filterName) && mapping.urlPattern.equals(urlPattern) && mapping.dispatcherType.equals(dispatcherType)) { result = true; } return result; } @Override public DispatcherType getDispatcherType() { return dispatcherType; } @Override public String getFilterName() { return filterName; } @Override public String getUrlPattern() { return urlPattern; } @Override public int hashCode() { int hash = 7; hash = 83 * hash + Objects.hashCode(this.filterName); hash = 83 * hash + Objects.hashCode(this.urlPattern); return hash; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultHttpHeader.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.HttpHeader; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; /** * The default HttpHeader. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultHttpHeader implements HttpHeader { /** * Stores the name. */ private final String name; /** * Stores the values. */ private final ArrayList values = new ArrayList<>(); /** * Constructor. * * @param name the name. * @param value the value. */ public DefaultHttpHeader(String name, String value) { this.name = name; values.add(value); } /** * Add the value. * * @param value the value to add. */ @Override public void addValue(String value) { values.add(value); } /** * {@return the name} */ @Override public String getName() { return this.name; } /** * {@return the value} */ @Override public String getValue() { return values.get(0); } /** * {@return the values} */ @Override public Enumeration getValues() { return Collections.enumeration(values); } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultHttpHeaderManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.HttpHeaderManager; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Locale; /** * The default HttpHeaderManager. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultHttpHeaderManager implements HttpHeaderManager { /** * Stores the headers. */ protected final HashMap headers; /** * Stores the Locale. */ private Locale locale; /** * Constructor. */ @SuppressWarnings("deprecation") public DefaultHttpHeaderManager() { headers = new HashMap<>(); locale = new Locale("en", "US", "ISO-8859-1"); } @Override public void addHeader(String name, String value) { if (headers.containsKey(name.toUpperCase(locale))) { headers.get(name.toUpperCase(locale)).addValue(value); } else { DefaultHttpHeader header = new DefaultHttpHeader(name, value); headers.put(name.toUpperCase(locale), header); } } @Override public boolean containsHeader(String name) { return headers.containsKey(name.toUpperCase(locale)); } @Override public long getDateHeader(String name) throws IllegalArgumentException { long result = -1; if (headers.containsKey(name.toUpperCase(locale))) { DefaultHttpHeader header = headers.get(name.toUpperCase(locale)); try { String value = header.getValue(); SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", locale); result = format.parse(value).getTime(); } catch (ParseException exception) { throw new IllegalArgumentException( "Cannot convert header to a date", exception); } } return result; } @Override public String getHeader(String name) { String result = null; if (headers.containsKey(name.toUpperCase(locale))) { result = headers.get(name.toUpperCase(locale)).getValue(); } return result; } @Override public Enumeration getHeaderNames() { List names = new ArrayList<>(); for (DefaultHttpHeader header : headers.values()) { names.add(header.getName()); } return Collections.enumeration(names); } @Override public Enumeration getHeaders(String name) { Enumeration result = Collections.enumeration(Collections.emptyList()); if (headers.containsKey(name.toUpperCase(locale))) { result = headers.get(name.toUpperCase(locale)).getValues(); } return result; } @Override public int getIntHeader(String name) throws NumberFormatException { int result = -1; if (headers.containsKey(name.toUpperCase(locale))) { DefaultHttpHeader header = headers.get(name.toUpperCase(locale)); try { result = Integer.parseInt(header.getValue()); } catch (NumberFormatException exception) { throw new NumberFormatException( "Cannot convert header to an int"); } } return result; } @Override public void removeHeader(String name) { headers.remove(name.toUpperCase()); } @Override public void setHeader(String name, String value) { DefaultHttpHeader header = new DefaultHttpHeader(name, value); headers.put(name.toUpperCase(locale), header); } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultHttpServletMapping.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import jakarta.servlet.http.HttpServletMapping; import jakarta.servlet.http.MappingMatch; /** * The default HttpServletMapping. * * @author Arjan Tijms */ public class DefaultHttpServletMapping implements HttpServletMapping { /** * Stores the MappingMatch */ private MappingMatch mappingMatch; /** * Stores the matchValue * */ private String matchValue; /** * Stores the pattern */ private String pattern; /** * Stores the servletName */ private String servletName; /** * Constructor. */ public DefaultHttpServletMapping() { } @Override public MappingMatch getMappingMatch() { return mappingMatch; } /** * Set the mapping match. * * @param mappingMatch the mappingMatch to set */ public void setMappingMatch(MappingMatch mappingMatch) { this.mappingMatch = mappingMatch; } @Override public String getMatchValue() { return matchValue; } /** * Set the matchValue. * * @param matchValue the matchValue to set */ public void setMatchValue(String matchValue) { this.matchValue = matchValue; } @Override public String getPattern() { return pattern; } /** * Set the pattern. * * @param pattern the pattern to set */ public void setPattern(String pattern) { this.pattern = pattern; } @Override public String getServletName() { return servletName; } /** * Set the servlet name. * * @param servletName the servletName to set */ public void setServletName(String servletName) { this.servletName = servletName; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultHttpSession.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.HttpSessionManager; import jakarta.servlet.ServletContext; import jakarta.servlet.http.HttpSession; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.UUID; /** * The default HttpSession. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultHttpSession implements HttpSession { /** * Stores the attributes. */ private final HashMap attributes = new HashMap<>(); /** * Stores the creation time. */ private final long creationTime; /** * Stores the session id. */ private String id; /** * Stores the last accessed time. */ private long lastAccessedTime; /** * Stores the max inactive interval. */ private int maxInactiveInterval; /** * Stores if the session is new. */ private boolean newFlag; /** * Stores the servlet context. */ private ServletContext servletContext; /** * Stores the HTTP session manager. */ private HttpSessionManager sessionManager; /** * Stores the valid flag. */ private boolean valid; /** * Constructor. * * @param servletContext the servlet context. */ public DefaultHttpSession(ServletContext servletContext) { this.id = UUID.randomUUID().toString(); this.servletContext = servletContext; this.creationTime = System.currentTimeMillis(); this.lastAccessedTime = System.currentTimeMillis(); this.valid = true; } /** * Constructor. * * @param servletContext the servlet context. * @param id the id. * @param newFlag the new flag. */ public DefaultHttpSession(ServletContext servletContext, String id, boolean newFlag) { this.id = id; this.servletContext = servletContext; this.newFlag = newFlag; this.creationTime = System.currentTimeMillis(); this.lastAccessedTime = System.currentTimeMillis(); this.valid = true; } @Override public Object getAttribute(String name) { verifyValid("getAttribute"); return this.attributes.get(name); } @Override public Enumeration getAttributeNames() { verifyValid("getAttributeNames"); return Collections.enumeration(attributes.keySet()); } @Override public long getCreationTime() { verifyValid("getCreationTime"); return this.creationTime; } @Override public String getId() { return this.id; } @Override public long getLastAccessedTime() { verifyValid("getLastAccessedTime"); return this.lastAccessedTime; } @Override public int getMaxInactiveInterval() { return this.maxInactiveInterval; } @Override public ServletContext getServletContext() { return servletContext; } @Override public void invalidate() { verifyValid("invalidate"); sessionManager.destroySession(this); this.valid = false; } @Override public boolean isNew() { verifyValid("isNew"); return this.newFlag; } @Override public void removeAttribute(String name) { verifyValid("removeAttribute"); sessionManager.attributeRemoved(this, name, attributes.remove(name)); } @Override public void setAttribute(String name, Object value) { verifyValid("setAttribute"); if (value != null) { boolean added = true; if (attributes.containsKey(name)) { added = false; } Object oldValue = attributes.put(name, value); if (added) { sessionManager.attributeAdded(this, name, value); } else { sessionManager.attributeReplaced(this, name, oldValue, value); } } else { removeAttribute(name); } } /** * Set the id. * * @param id the id. */ public void setId(String id) { this.id = id; } @Override public void setMaxInactiveInterval(int maxInactiveInterval) { this.maxInactiveInterval = maxInactiveInterval; } /** * Set the new flag. * * @param newFlag the new flag. */ public void setNew(boolean newFlag) { verifyValid("setNew"); this.newFlag = newFlag; } /** * Set the HTTP session manager. * * @param sessionManager the HTTP session manager. */ public void setSessionManager(HttpSessionManager sessionManager) { this.sessionManager = sessionManager; } /** * Verify if the session is valid. * * @param methodName the method name. */ private void verifyValid(String methodName) { if (!valid) { throw new IllegalStateException("Session is invalid, called by: " + methodName); } } /** * Set the last accessed time. * * @param lastAccessedTime the last accessed time. */ public void setLastAccessedTime(long lastAccessedTime) { this.lastAccessedTime = lastAccessedTime; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultHttpSessionManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.HttpSessionManager; import cloud.piranha.core.api.WebApplication; import jakarta.servlet.ServletRequestEvent; import jakarta.servlet.ServletRequestListener; import jakarta.servlet.SessionCookieConfig; import jakarta.servlet.SessionTrackingMode; import static jakarta.servlet.SessionTrackingMode.COOKIE; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpSessionAttributeListener; import jakarta.servlet.http.HttpSessionBindingEvent; import jakarta.servlet.http.HttpSessionBindingListener; import jakarta.servlet.http.HttpSessionEvent; import jakarta.servlet.http.HttpSessionIdListener; import jakarta.servlet.http.HttpSessionListener; import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.EventListener; import java.util.HashMap; import java.util.Iterator; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; import static java.util.concurrent.TimeUnit.MILLISECONDS; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import java.util.stream.Stream; /** * The default HttpSessionManager. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultHttpSessionManager implements HttpSessionManager, SessionCookieConfig, ServletRequestListener { /** * Stores the session listeners. */ protected final ArrayList attributeListeners; /** * Stores the comment. */ protected String comment; /** * Stores the default session tracking modes. */ protected final Set defaultSessionTrackingModes; /** * Stores the domain. */ protected String domain; /** * Stores the HTTP only flag. */ protected boolean httpOnly; /** * Stores the session id listeners. */ protected final ArrayList idListeners; /** * Stores the max age. */ protected int maxAge; /** * Stores the name. */ protected String name; /** * Stores the path. */ protected String path; /** * Stores the secure flag. */ protected boolean secure; /** * Stores the session counters. */ protected final Map sessionCounters = new HashMap<>(); /** * Stores the cookie attributes. */ protected HashMap sessionCookieAttributes; /** * Stores the session listeners. */ protected final ArrayList sessionListeners; /** * Stores the session timeout (in minutes). */ protected int sessionTimeout; /** * Stores the session tracking modes. */ protected Set sessionTrackingModes; /** * Stores the sessions. */ protected Map sessions; /** * Stores the web application. */ protected WebApplication webApplication; /** * Constructor. */ public DefaultHttpSessionManager() { attributeListeners = new ArrayList<>(1); sessionCookieAttributes = new HashMap<>(); defaultSessionTrackingModes = EnumSet.of(COOKIE); sessionTrackingModes = defaultSessionTrackingModes; idListeners = new ArrayList<>(1); name = "JSESSIONID"; sessionListeners = new ArrayList<>(1); sessionTimeout = 10; maxAge = -1; sessions = new ConcurrentHashMap<>(); ThreadFactory threadFactory = (Runnable r) -> { Thread thread = new Thread(r); thread.setDaemon(true); return thread; }; ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, threadFactory); scheduler.scheduleWithFixedDelay(this::reapSessions, 0, 1300, MILLISECONDS); } @Override public void addListener(T listener) { if (listener instanceof HttpSessionAttributeListener httpSessionAttributeListener) { attributeListeners.add(httpSessionAttributeListener); } if (listener instanceof HttpSessionIdListener httpSessionIdListener) { idListeners.add(httpSessionIdListener); } if (listener instanceof HttpSessionListener httpSessionListener) { sessionListeners.add(httpSessionListener); } } @Override public void attributeAdded(HttpSession session, String name, Object value) { attributeListeners.stream().forEach(listener -> listener.attributeAdded(new HttpSessionBindingEvent(session, name, value))); if (value instanceof HttpSessionBindingListener httpSessionBindingListener) { httpSessionBindingListener.valueBound(new HttpSessionBindingEvent(session, name)); } } @Override public void attributeRemoved(HttpSession session, String name, Object value) { attributeListeners.stream().forEach(listener -> listener.attributeRemoved(new HttpSessionBindingEvent(session, name, value))); if (value instanceof HttpSessionBindingListener httpSessionBindingListener) { httpSessionBindingListener.valueUnbound(new HttpSessionBindingEvent(session, name)); } } @Override public void attributeReplaced(HttpSession session, String name, Object oldValue, Object newValue) { attributeListeners.stream().forEach(listener -> listener.attributeReplaced(new HttpSessionBindingEvent(session, name, oldValue))); if (oldValue instanceof HttpSessionBindingListener httpSessionBindingListener) { httpSessionBindingListener.valueUnbound(new HttpSessionBindingEvent(session, name)); } if (newValue instanceof HttpSessionBindingListener httpSessionBindingListener) { httpSessionBindingListener.valueBound(new HttpSessionBindingEvent(session, name)); } } @Override public String changeSessionId(HttpServletRequest request) { HttpSession session = request.getSession(false); if (session == null) { throw new IllegalStateException("No session active"); } String oldSessionId = session.getId(); sessions.remove(oldSessionId); String sessionId = UUID.randomUUID().toString(); DefaultHttpSession newSession = (DefaultHttpSession) session; newSession.setId(sessionId); sessions.put(sessionId, session); idListeners.stream().forEach(idListener -> idListener.sessionIdChanged(new HttpSessionEvent(session), oldSessionId)); return sessionId; } @Override @SuppressWarnings({"removal"}) public synchronized HttpSession createSession(HttpServletRequest request) { String sessionId = UUID.randomUUID().toString(); DefaultHttpSession session = new DefaultHttpSession(webApplication, sessionId, true); session.setMaxInactiveInterval(getSessionTimeout() * 60); session.setSessionManager(this); sessions.put(sessionId, session); sessionCounters.put(session.getId(), new AtomicInteger(1)); HttpServletResponse response = (HttpServletResponse) webApplication.getResponse(request); Cookie cookie = new Cookie(name, sessionId); if (path != null) { cookie.setPath(path); } else { cookie.setPath("".equals(request.getContextPath()) ? "/" : request.getContextPath()); } cookie.setComment(comment); if (domain != null) { cookie.setDomain(domain); } cookie.setHttpOnly(httpOnly); cookie.setMaxAge(maxAge); cookie.setSecure(secure); response.addCookie(cookie); sessionListeners.stream().forEach(sessionListener -> sessionListener.sessionCreated(new HttpSessionEvent(session))); return session; } @Override public synchronized void destroySession(HttpSession session) { for (HttpSessionListener sessionListener : sessionListeners) { sessionListener.sessionDestroyed(new HttpSessionEvent(session)); } sessions.remove(session.getId()); sessionCounters.remove(session.getId()); Iterator attributeNames = session.getAttributeNames().asIterator(); while(attributeNames.hasNext()) { String attributeName = attributeNames.next(); attributeRemoved(session, attributeName, session.getAttribute(attributeName)); } } @Override public String encodeRedirectURL(HttpServletResponse response, String url) { return url; } @Override public String encodeURL(HttpServletResponse response, String url) { return url; } @Override @SuppressWarnings("removal") public String getComment() { return comment; } @Override public Set getDefaultSessionTrackingModes() { return Collections.unmodifiableSet(defaultSessionTrackingModes); } @Override public String getDomain() { return domain; } @Override public Set getEffectiveSessionTrackingModes() { return Collections.unmodifiableSet(sessionTrackingModes); } @Override public int getMaxAge() { return maxAge; } @Override public String getName() { return name; } @Override public String getPath() { return path; } @Override public HttpSession getSession(HttpServletRequest request, String currentSessionId) { return sessions.get(currentSessionId); } @Override public SessionCookieConfig getSessionCookieConfig() { return this; } @Override public int getSessionTimeout() { return sessionTimeout; } @Override public boolean hasSession(String sessionId) { boolean result = false; if (sessionId != null) { result = sessions.containsKey(sessionId); } return result; } @Override public boolean isHttpOnly() { return httpOnly; } @Override public boolean isSecure() { return secure; } /** * Reap any inactive session. */ protected void reapSessions() { ArrayList keys = new ArrayList<>(); keys.addAll(sessions.keySet()); keys.forEach(sessionId -> { HttpSession session = sessions.get(sessionId); if (session != null) { synchronized (session) { if (sessionCounters.getOrDefault(session.getId(), new AtomicInteger(0)).intValue() <= 0) { try { if (session.getLastAccessedTime() + (session.getMaxInactiveInterval() * 1000L) - 1300 < System.currentTimeMillis()) { session.invalidate(); } } catch (IllegalStateException ise) { // nothing to do } } } } }); } @Override @SuppressWarnings("removal") public void setComment(String comment) { if (webApplication.isInitialized()) { throw new IllegalStateException("You cannot call setComment once ServletContext is initialized"); } } @Override public void setDomain(String domain) { if (webApplication.isInitialized()) { throw new IllegalStateException("You cannot call setDomain once ServletContext is initialized"); } this.domain = domain; } @Override public void setHttpOnly(boolean httpOnly) { if (webApplication.isInitialized()) { throw new IllegalStateException("You cannot call setHttpOnly once ServletContext is initialized"); } this.httpOnly = httpOnly; } @Override public void setMaxAge(int maxAge) { if (webApplication.isInitialized()) { throw new IllegalStateException("You cannot call setMaxAge once ServletContext is initialized"); } this.maxAge = maxAge; } @Override public void setName(String name) { if (webApplication.isInitialized()) { throw new IllegalStateException("You cannot call setName once ServletContext is initialized"); } this.name = name; } @Override public void setPath(String path) { if (webApplication.isInitialized()) { throw new IllegalStateException("You cannot call setPath once ServletContext is initialized"); } this.path = path; } @Override public void setSecure(boolean secure) { if (webApplication.isInitialized()) { throw new IllegalStateException("You cannot call setSecure once ServletContext is initialized"); } this.secure = secure; } @Override public void setSessionTimeout(int sessionTimeout) { this.sessionTimeout = sessionTimeout; } @Override public void setSessionTrackingModes(Set sessionTrackingModes) { if (sessionTrackingModes.size() > 1 && sessionTrackingModes.contains(SessionTrackingMode.SSL)) { throw new IllegalArgumentException("SSL cannot be combined with any other method"); } this.sessionTrackingModes = Collections.unmodifiableSet(sessionTrackingModes); } @Override public void setWebApplication(WebApplication webApplication) { this.webApplication = webApplication; } @Override public void requestInitialized(ServletRequestEvent event) { if (event.getServletRequest() instanceof HttpServletRequest httpRequest) { HttpSession session = httpRequest.getSession(false); if (session != null) { synchronized (session) { AtomicInteger integer = sessionCounters.get(session.getId()); if (integer != null) { integer.incrementAndGet(); } } } } } @Override public void requestDestroyed(ServletRequestEvent event) { if (event.getServletRequest() instanceof HttpServletRequest httpRequest) { HttpSession session = httpRequest.getSession(false); if (session != null) { synchronized (session) { AtomicInteger integer = sessionCounters.get(session.getId()); if (integer != null) { integer.decrementAndGet(); } } } } } /* REVIEW FOR SERVLET 6 */ @Override public void setAttribute(String name, String value) { if (name == null) { throw new IllegalArgumentException("name is null"); } if (webApplication.isInitialized()) { throw new IllegalStateException("You cannot call setAttribute once ServletContext is initialized"); } String nameLowerCase = name.toLowerCase(Locale.ROOT); switch (nameLowerCase) { case "comment" -> setComment(value); case "path" -> setPath(value); case "name" -> setName(value); case "domain" -> setDomain(value); case "max-age" -> setMaxAge(Integer.parseInt(value)); case "secure" -> setSecure(Boolean.parseBoolean(value)); case "httponly" -> setHttpOnly(Boolean.parseBoolean(value)); default -> sessionCookieAttributes.put(name, value); } } /* REVIEW FOR SERVLET 6 */ @Override public String getAttribute(String name) { String nameLowerCase = name.toLowerCase(Locale.ROOT); return switch (nameLowerCase) { case "comment" -> getComment(); case "path" -> getPath(); case "name" -> getName(); case "domain" -> getDomain(); case "max-age" -> String.valueOf(getMaxAge()); case "secure" -> String.valueOf(isSecure()); case "httponly" -> String.valueOf(isHttpOnly()); default -> sessionCookieAttributes.get(name); }; } @Override public Map getAttributes() { Stream> entriesWithGettersAndSetter = Stream.of( Map.entry("Comment", getComment()), Map.entry("Path", getPath()), Map.entry("Name", getName()), Map.entry("Domain", getDomain()), Map.entry("Max-Age", String.valueOf(getMaxAge())), Map.entry("HttpOnly", String.valueOf(isHttpOnly())), Map.entry("Secure", String.valueOf(isSecure())) ); return Stream.concat(entriesWithGettersAndSetter, sessionCookieAttributes.entrySet().stream()) .collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue)); } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultInvocationFinder.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import static jakarta.servlet.DispatcherType.REQUEST; import static jakarta.servlet.http.MappingMatch.DEFAULT; import static jakarta.servlet.http.MappingMatch.EXACT; import static jakarta.servlet.http.MappingMatch.EXTENSION; import static jakarta.servlet.http.MappingMatch.PATH; import static java.util.Collections.reverse; import java.io.IOException; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import cloud.piranha.core.api.FilterEnvironment; import cloud.piranha.core.api.FilterPriority; import cloud.piranha.core.api.ServletEnvironment; import cloud.piranha.core.api.WebApplicationRequestMapping; import jakarta.servlet.DispatcherType; import jakarta.servlet.FilterChain; import jakarta.servlet.Servlet; import jakarta.servlet.ServletException; import java.lang.System.Logger; import static java.lang.System.Logger.Level.TRACE; /** * The invocation finder tries to find a servlet invocation matching a request * for a path based or name based dispatch. * *

* Invocations returned by this finder take into account the various mappings, * filters, welcome files and the default servlet. * * @author Arjan Tijms */ public class DefaultInvocationFinder { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(DefaultInvocationFinder.class.getName()); /** * Stores the web application. */ private final DefaultWebApplication webApplication; /** * Constructor. * * @param webApplication the web application. */ public DefaultInvocationFinder(DefaultWebApplication webApplication) { this.webApplication = webApplication; } /** * Find the servlet invocation by path. * * @param servletPath the servlet path. * @param pathInfo the path info. * @return the servlet invocation. * @throws IOException when an I/O error occurs. * @throws ServletException when a Servlet error occurs. */ public DefaultServletInvocation findServletInvocationByPath(String servletPath, String pathInfo) throws IOException, ServletException { return findServletInvocationByPath(REQUEST, servletPath, pathInfo); } /** * Find the servlet invocation by path. * * @param dispatcherType the dispatcher type. * @param servletPath the servlet path. * @param pathInfo the path info. * @return the servlet invocation. * @throws IOException when an I/O error occurs. * @throws ServletException when a Servlet error occurs. */ public DefaultServletInvocation findServletInvocationByPath(DispatcherType dispatcherType, String servletPath, String pathInfo) throws IOException, ServletException { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Find servlet invocation by path"); LOGGER.log(TRACE, "DispatcherType: {0}", dispatcherType.name()); LOGGER.log(TRACE, "Servlet path: {0}", servletPath); LOGGER.log(TRACE, "Path info: {0}", pathInfo); } DefaultServletInvocation servletInvocation = getDirectServletInvocationByPath(servletPath, pathInfo); if (servletInvocation == null) { if (dispatcherType == REQUEST) { servletInvocation = getWelcomeFileServletInvocation(servletPath, pathInfo != null ? pathInfo : "/"); if (servletInvocation == null) { String servletPathLower = servletPath != null ? servletPath.toLowerCase() : ""; if (servletPathLower.startsWith("/web-inf") || servletPathLower.startsWith("/meta-inf")) { servletInvocation = getDefaultServletInvocation(null, null); } else { servletInvocation = getDefaultServletInvocation(servletPath, pathInfo); } } } else { // Note: no WEB-INF checks needed here servletInvocation = getDefaultServletInvocation(servletPath, pathInfo); } } // Seed the chain with the servlet, if any. REQUEST dispatches can be done to only a filter so a servlet is not hard requirement servletInvocation.seedFilterChain(); return addFilters(dispatcherType, servletInvocation, servletPath, pathInfo); } /** * Add the filters. * * @param dispatcherType the dispatcher type. * @param servletInvocation the servlet invocation. * @param servletPath the servlet path. * @param pathInfo the path info. * @return the servlet invocation. */ public DefaultServletInvocation addFilters(DispatcherType dispatcherType, DefaultServletInvocation servletInvocation, String servletPath, String pathInfo) { if (dispatcherType == null) { // If there's no dispatcher type, don't add filters. This can happen when the dispatch is not yet known // so as with the request dispatcher, which first gets the resource, and only after that gets to be used for a forward or include. return servletInvocation; } List filterEnvironments = findFilterEnvironments( dispatcherType, // Look at the servletInvocation if there is one, as a welcome file can be set // that differs from the request path servletInvocation == null ? servletPath : servletInvocation.getServletPath(), servletInvocation == null ? pathInfo : servletInvocation.getPathInfo(), servletInvocation == null ? null : servletInvocation.getServletName()); if (filterEnvironments != null) { if (servletInvocation == null) { servletInvocation = new DefaultServletInvocation(); servletInvocation.setServletPath(servletPath); servletInvocation.setPathInfo(pathInfo); } servletInvocation.setFilterEnvironments(filterEnvironments); servletInvocation.setFilterChain(findFilterChain(filterEnvironments, servletInvocation.getFilterChain())); } return servletInvocation; } /** * Find the servlet invocation by servlet name. * * @param servletName the servlet name. * @return the servlet invocation, or null if not found. */ public DefaultServletInvocation findServletInvocationByName(String servletName) { ServletEnvironment servletEnvironment = webApplication.servletEnvironments.get(servletName); if (servletEnvironment == null) { return null; } DefaultServletInvocation servletInvocation = new DefaultServletInvocation(); servletInvocation.setServletName(servletName); servletInvocation.setServletEnvironment(servletEnvironment); servletInvocation.seedFilterChain(); servletInvocation.setFromNamed(true); return servletInvocation; } private DefaultServletInvocation getDirectServletInvocationByPath(String servletPath, String pathInfo) { String path = addOrRemoveSlashIfNeeded(servletPath + (pathInfo == null ? "" : pathInfo)); WebApplicationRequestMapping mapping = webApplication.webApplicationRequestMapper.findServletMapping(path); if (mapping == null) { return null; } String servletName = webApplication.webApplicationRequestMapper.getServletName(mapping.getPattern()); if (servletName == null) { return null; } ServletEnvironment servletEnvironment = webApplication.servletEnvironments.get(servletName); if (servletEnvironment == null) { return null; } DefaultServletInvocation servletInvocation = new DefaultServletInvocation(); servletInvocation.setInvocationPath(path); servletInvocation.setApplicationRequestMapping(mapping); servletInvocation.setServletName(servletName); servletInvocation.setServletEnvironment(servletEnvironment); servletInvocation.getHttpServletMapping().setMappingMatch( mapping.isExact() ? EXACT : mapping.isExtension() ? EXTENSION : PATH); servletInvocation.getHttpServletMapping().setPattern(mapping.getPattern()); servletInvocation.getHttpServletMapping().setServletName(servletName); servletInvocation.getHttpServletMapping().setMatchValue(mapping.getMatchValue()); if (mapping.isExact()) { servletInvocation.setServletPath(path); servletInvocation.setPathInfo(null); } else if (!mapping.isExtension()) { servletInvocation.setServletPath(mapping.getPattern().substring(0, mapping.getPattern().length() - 2)); String newPathInfo = path.substring(mapping.getPattern().length() - 2); servletInvocation.setPathInfo(newPathInfo.isEmpty() ? null : newPathInfo); } else { servletInvocation.setServletPath(servletPath); servletInvocation.setPathInfo(pathInfo); } return servletInvocation; } private DefaultServletInvocation getWelcomeFileServletInvocation(String servletPath, String pathInfo) throws IOException { if (webApplication.getManager().getWelcomeFileManager() != null) { for (String welcomeFile : webApplication.getManager().getWelcomeFileManager().getWelcomeFileList()) { DefaultServletInvocation servletInvocation = null; if (isJsp(welcomeFile)) { // .jsp files are special in the system, as they are mapped to a servlet, but also // have to be present at exactly that path as static resource. Additionally we have // the required index.jsp welcome file, that may not actually be there. // So, .jsp files are treated as a servlet invocation but only if the static resource exists if (isStaticResource(servletPath, pathInfo + welcomeFile)) { servletInvocation = getDirectServletInvocationByPath(servletPath, pathInfo + welcomeFile); } } else if (isStaticResource(servletPath, pathInfo + welcomeFile)) { // If we have a welcome file, we can load it via the default servlet. return getDefaultServletInvocation(servletPath, pathInfo + welcomeFile); } else { // Try if we have a welcome servlet servletInvocation = getDirectServletInvocationByPath(servletPath, pathInfo + welcomeFile); } if (servletInvocation != null) { servletInvocation.setOriginalServletPath(servletPath); return servletInvocation; } } // No static file, JSP or servlet } return null; } private boolean isStaticResource(String servletPath, String pathInfo) throws MalformedURLException { return webApplication.getResource(addOrRemoveSlashIfNeeded(servletPath + (pathInfo == null ? "" : pathInfo))) != null; } private static boolean isJsp(String welcomeFile) { return welcomeFile.endsWith(".jsp"); } private String addOrRemoveSlashIfNeeded(String string) { if (string.startsWith("/")) { if (string.startsWith("//")) { return string.substring(1); } return string; } return "/" + string; } private DefaultServletInvocation getDefaultServletInvocation(String servletPath, String pathInfo) { ServletEnvironment servletEnvironment = null; String servletName = webApplication.webApplicationRequestMapper.getDefaultServlet(); if (servletName != null) { servletEnvironment = webApplication.servletEnvironments.get(servletName); } if (servletEnvironment == null) { Servlet defaultServlet = webApplication.defaultServlet; if (defaultServlet == null) { defaultServlet = new DefaultServlet(); } servletName = "default"; servletEnvironment = new DefaultServletEnvironment(webApplication, servletName, defaultServlet); } DefaultServletInvocation servletInvocation = new DefaultServletInvocation(); servletInvocation.setServletName(servletName); servletInvocation.setServletEnvironment(servletEnvironment); // 12.2 // A string containing only the "/" character indicates the "default" servlet of the application. // In this case the servlet path is the request URI minus the context path and the path info is null. servletInvocation.setServletPath(servletPath == null ? null : addOrRemoveSlashIfNeeded(servletPath + (pathInfo == null ? "" : pathInfo))); servletInvocation.setInvocationPath(servletPath); // look at whether its really needed to have path and invocation path servletInvocation.setPathInfo(null); servletInvocation.getHttpServletMapping().setMappingMatch(DEFAULT); servletInvocation.getHttpServletMapping().setMatchValue(""); servletInvocation.getHttpServletMapping().setPattern("/"); servletInvocation.getHttpServletMapping().setServletName(servletName); return servletInvocation; } /** * Find the filter environments. * * @param dispatcherType the dispatcher type. * @param servletPath the servlet path to which filters should apply. * @param pathInfo the path info to which filters should apply. * @param servletName name of the servlet to be filtered, if any. Can be * null. * * @return the filter environments. */ protected List findFilterEnvironments(DispatcherType dispatcherType, String servletPath, String pathInfo, String servletName) { List filterEnvironments = null; String path = servletPath + (pathInfo == null ? "" : pathInfo); Collection filterNames = webApplication.webApplicationRequestMapper.findFilterMappings(dispatcherType, path); if (servletName != null) { String servletNamePath = "servlet:// " + servletName; filterNames.addAll(webApplication.webApplicationRequestMapper.findFilterMappings(dispatcherType, servletNamePath)); } if (!filterNames.isEmpty()) { filterEnvironments = new ArrayList<>(); for (String filterName : filterNames) { if (webApplication.filters.get(filterName) != null) { filterEnvironments.add(webApplication.filters.get(filterName)); } } } return filterEnvironments; } private FilterChain findFilterChain(List filterEnvironments, FilterChain initialFilterChain) { List prioritisedFilters = filterEnvironments.stream() .filter(e -> e.getFilter() instanceof FilterPriority) .sorted(this::sortOnPriority) .toList(); List notPrioritisedFilters = filterEnvironments.stream() .filter(e -> !(e.getFilter() instanceof FilterPriority)) .toList(); List currentEnvironments = new ArrayList<>(); currentEnvironments.addAll(prioritisedFilters); currentEnvironments.addAll(notPrioritisedFilters); reverse(currentEnvironments); FilterChain downFilterChain = initialFilterChain; FilterChain upFilterChain; for (FilterEnvironment filterEnvironment : currentEnvironments) { upFilterChain = new DefaultFilterChain(filterEnvironment.getFilter(), downFilterChain); downFilterChain = upFilterChain; } return downFilterChain; } private int sortOnPriority(FilterEnvironment x, FilterEnvironment y) { FilterPriority filterX = (FilterPriority) x.getFilter(); FilterPriority filterY = (FilterPriority) y.getFilter(); return Integer.compare(filterX.getPriority(), filterY.getPriority()); } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultJspConfigDescriptor.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import jakarta.servlet.descriptor.JspConfigDescriptor; import jakarta.servlet.descriptor.JspPropertyGroupDescriptor; import jakarta.servlet.descriptor.TaglibDescriptor; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * The default JspConfigDescriptor. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultJspConfigDescriptor implements JspConfigDescriptor { /** * Stores the taglibs. */ private List taglibs = new ArrayList<>(); /** * Constructor. */ public DefaultJspConfigDescriptor() { } @Override public Collection getTaglibs() { return taglibs; } @Override public Collection getJspPropertyGroups() { return new ArrayList<>(); } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultJspManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.JspManager; import cloud.piranha.core.api.WebApplication; import jakarta.servlet.ServletRegistration; import jakarta.servlet.descriptor.JspConfigDescriptor; /** * The default JspManager. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultJspManager implements JspManager { /** * Stores the JSP config descriptor. */ private JspConfigDescriptor descriptor; /** * Constructor. */ public DefaultJspManager() { } @Override public ServletRegistration.Dynamic addJspFile(WebApplication webApplication, String servletName, String jspFile) { return null; } @Override public JspConfigDescriptor getJspConfigDescriptor() { return descriptor; } @Override public void setJspConfigDescriptor(JspConfigDescriptor descriptor) { this.descriptor = descriptor; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultLocaleEncodingManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.LocaleEncodingManager; import java.util.HashMap; import java.util.Map; /** * The default LocaleEncodingManager. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultLocaleEncodingManager implements LocaleEncodingManager { /** * Stores the locale mappings. */ private final Map localeMappings; /** * Constructor. */ public DefaultLocaleEncodingManager() { localeMappings = new HashMap<>(1); /* * TODO - We have added the "ja" locale as a default locale encoding * mapping for now as the TCK test seems the require it. Note * that we should evaluate if the TCK test should be * challenged. * * See com/sun/ts/tests/servlet/api/jakarta_servlet_http/httpservletresponse/URLClient.java#setCharacterEncodingTest */ localeMappings.put("ja", "Shift_Jis"); } @Override public void addCharacterEncoding(String locale, String encoding) { localeMappings.put(locale, encoding); } @Override public String getCharacterEncoding(String locale) { return localeMappings.get(locale); } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultModuleFinder.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.resource.api.Resource; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleFinder; import java.lang.module.ModuleReference; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.lang.System.Logger.Level; import java.lang.System.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import static java.lang.System.Logger.Level.DEBUG; import static java.lang.System.Logger.Level.WARNING; import static java.util.stream.Collectors.toSet; /** * Provides an implementation of {@link ModuleFinder} to work * with {@link Resource} * @author Thiago Henrique Hupner */ public class DefaultModuleFinder implements ModuleFinder { /** * Stores the logger */ private static final Logger LOGGER = System.getLogger(DefaultModuleFinder.class.getName()); /** * Stores the attribute Automatic-Module-Name */ private static final Attributes.Name AUTOMATIC_MODULE_NAME = new Attributes.Name("Automatic-Module-Name"); /** * Stores the dash version pattern */ private static final Pattern DASH_VERSION = Pattern.compile("-(\\d+(\\.|$))"); /** * Stores the non alphanumeric pattern */ private static final Pattern NON_ALPHANUMERIC = Pattern.compile("[^A-Za-z0-9]"); /** * Stores the repeating dots pattern */ private static final Pattern REPEATING_DOTS = Pattern.compile("(\\.)(\\1)+"); /** * Stores the leading dots pattern */ private static final Pattern LEADING_DOTS = Pattern.compile("^\\."); /** * Stores the trailing dots pattern */ private static final Pattern TRAILING_DOTS = Pattern.compile("\\.$"); /** * Stores the cache for the modules already resolved */ private final Map cachedModuleReferences = new HashMap<>(); /** * Store the resources */ private final List resources; /** * Stores the searched flag */ private boolean searched = false; /** * Constructor * @param resources the resources */ public DefaultModuleFinder(List resources) { this.resources = resources; } @Override public Optional find(String name) { ModuleReference moduleReference = cachedModuleReferences.get(name); if (moduleReference != null) { return Optional.of(moduleReference); } searchAllModules(); return Optional.ofNullable(cachedModuleReferences.get(name)); } private String getModuleName(Resource resource, String resourceName) { String automaticModuleName = deriveAutomaticModuleNameFromManifest(resource); return normalizeModuleName(automaticModuleName != null ? automaticModuleName : cleanModuleName(resourceName)); } private ModuleDescriptor moduleDescriptorFromResource(Resource resource) { ModuleDescriptor moduleInfo = moduleInfo(resource); if (moduleInfo != null){ if (LOGGER.isLoggable(DEBUG)) { LOGGER.log(DEBUG, () -> "Module " + moduleInfo.toNameAndVersion() + " from resource: " + resource.getName() + " (module-info.class)"); LOGGER.log(DEBUG,() -> "Package exported by module " + moduleInfo.name() + ": " + moduleInfo.exports()); moduleInfo.provides().stream().map(x -> "Module provides " + x.service() + "with " + x.providers()).forEach(x -> LOGGER.log(DEBUG, x)); moduleInfo.uses().stream().map(x -> "Module uses " + x).forEach(x -> LOGGER.log(DEBUG, x)); } return moduleInfo; } String name = resource.getName().replace(".jar", ""); String versionString = null; // find first occurrence of -${NUMBER}. or -${NUMBER}$ Matcher matcher = DASH_VERSION.matcher(name); if (matcher.find()) { int start = matcher.start(); // attempt to parse the tail as a version string try { String tail = name.substring(start + 1); ModuleDescriptor.Version.parse(tail); versionString = tail; } catch (IllegalArgumentException ignore) { // nothing to do here. } name = name.substring(0, start); } String moduleName = getModuleName(resource, name); String version = versionString; LOGGER.log(DEBUG, () -> "Module " + moduleName + ((version != null)? "@" + version : "") + " from " + resource.getName()); ModuleDescriptor.Builder builder = ModuleDescriptor.newAutomaticModule(moduleName); if (version != null) builder.version(version); Set packages = packages(resource); LOGGER.log(DEBUG, () -> "Packages exported by module " + moduleName + ": " + packages); builder.packages(packages); addProviders(builder, resource); return builder.build(); } private void addProviders(ModuleDescriptor.Builder builder, Resource resource) { Set providers = resource.getAllLocations() .filter(x -> x.startsWith("/META-INF/services/") && !x.endsWith("/")) .collect(toSet()); for (String providerFile : providers) { InputStream inputStream = resource.getResourceAsStream(providerFile); if (inputStream == null) continue; @SuppressWarnings("resource") List providerList = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)) .lines() .filter(x -> !x.startsWith("#")) .filter(x -> !x.isEmpty()) .toList(); if (!providerList.isEmpty()) { String serviceName = providerFile.substring("/META-INF/services/".length()); LOGGER.log(DEBUG, () -> "Module provides " + serviceName + " with " + providerList); builder.provides(serviceName, providerList); } } } private Set packages(Resource resource) { return resource.getAllLocations() .filter(x -> x.endsWith(".class")) .filter(x -> !x.startsWith("/META-INF")) .map(x -> x.substring(1)) .map(this::toPackageName) .filter(Optional::isPresent) .map(Optional::get) .collect(toSet()); } private String normalizeModuleName(String module) { StringBuilder result = new StringBuilder(); for (String part : module.split("\\.")) { if (!Character.isJavaIdentifierStart(part.charAt(0))) { result.append("$").append(part); } else { result.append(part); } result.append("."); } return result.deleteCharAt(result.length() - 1).toString(); } private Optional toPackageName(String name) { int index = name.lastIndexOf("/"); if (index > 0) { return Optional.of(name.substring(0, index).replace('/', '.')); } else { return Optional.empty(); } } private ModuleDescriptor moduleInfo(Resource resource) { // Need to parse MRJARs try (InputStream moduleInfo = resource.getResourceAsStream("module-info.class")) { if (moduleInfo != null) { // We have a module-info return ModuleDescriptor.read(moduleInfo, () -> packages(resource)); } } catch (IOException e) { // nothing to do here. } return null; } private String deriveAutomaticModuleNameFromManifest(Resource resource) { try (InputStream manifestInputStream = resource.getResourceAsStream("META-INF/MANIFEST.MF")) { if (manifestInputStream == null) return null; Manifest man = new Manifest(manifestInputStream); Attributes attrs = man.getMainAttributes(); if (attrs != null) { return attrs.getValue(AUTOMATIC_MODULE_NAME); } } catch (IOException e) { // nothing to do here. } return null; } private void searchAllModules() { if (searched) return; searched = true; Map packageToExporter = new HashMap<>(); for (Resource resource : resources) { try { ModuleDescriptor moduleDescriptor = moduleDescriptorFromResource(resource); if (hasSplitPackages(packageToExporter, moduleDescriptor)) { continue; } cachedModuleReferences.put(moduleDescriptor.name(), new DefaultModuleReference(moduleDescriptor, URI.create("resource://" + resource.getName()), resource)); } catch (Exception e) { LOGGER.log(Level.WARNING, () -> "Resource " + resource.getName() + " will not be treated as a module: " + e); } } } private boolean hasSplitPackages(Map packageToExporter, ModuleDescriptor moduleDescriptor) { for (String aPackage : moduleDescriptor.packages()) { String previousModuleName = packageToExporter.put(aPackage, moduleDescriptor.name()); if (previousModuleName != null) { LOGGER.log(WARNING, () -> "Modules %s and %s export package %s, they will be part of the unnamed module" .formatted(moduleDescriptor.name(), previousModuleName, aPackage)); cachedModuleReferences.remove(previousModuleName); packageToExporter.remove(aPackage); return true; } } return false; } @Override public Set findAll() { searchAllModules(); return Set.copyOf(cachedModuleReferences.values()); } /** * Clean up candidate module name derived from a JAR file name. * * @param mn the module name to cleanup. * @return the cleaned module name. */ private static String cleanModuleName(String mn) { // replace non-alphanumeric mn = NON_ALPHANUMERIC.matcher(mn).replaceAll("."); // collapse repeating dots mn = REPEATING_DOTS.matcher(mn).replaceAll("."); // drop leading dots if (!mn.isEmpty() && mn.charAt(0) == '.') mn = LEADING_DOTS.matcher(mn).replaceAll(""); // drop trailing dots int len = mn.length(); if (len > 0 && mn.charAt(len-1) == '.') mn = TRAILING_DOTS.matcher(mn).replaceAll(""); return mn; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultModuleLayerProcessor.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.ModuleLayerProcessor; import java.lang.System.Logger; import static java.lang.System.Logger.Level.WARNING; /** * The module layer processor * @author Thiago Henrique Hupner */ public enum DefaultModuleLayerProcessor implements ModuleLayerProcessor { /** * Singleton instance */ INSTANCE; /** * Stores the add opens property *

* The format is: module.source/package=module.target (, module.source/package=module.target)* *

*/ private static final String ADD_OPENS = "cloud.piranha.modular.add-opens"; /** * Stores the add opens property *

* The format is: module.source/package=module.target (, module.source/package=module.target)* *

*/ private static final String ADD_EXPORTS = "cloud.piranha.modular.add-exports"; /** * Stores the add opens property *

* The format is: module.source=module.target (, module.source=module.target)* *

*/ private static final String ADD_READS = "cloud.piranha.modular.add-reads"; /** * Stores the logger */ private static final Logger LOGGER = System.getLogger(DefaultModuleLayerProcessor.class.getName()); @Override public void processModuleLayerOptions(ModuleLayer.Controller controller) { ModuleLayer moduleLayer = controller.layer(); String opens = System.getProperty(ADD_OPENS); if (opens != null) { addExportsOrOpens(opens, moduleLayer, controller, true); } String exports = System.getProperty(ADD_EXPORTS); if (exports != null) { addExportsOrOpens(exports, moduleLayer, controller, false); } String reads = System.getProperty(ADD_READS); if (reads != null) { addReads(reads, moduleLayer, controller); } } private static void logModuleNotFound(String option, String module) { LOGGER.log(WARNING, () -> "Ignoring option " + option + " because module " + module + " was not found"); } private static void addReads(String property, ModuleLayer moduleLayer, ModuleLayer.Controller controller) { for (String readsOption : property.trim().split(",")) { // module.source=module.target String[] parts = readsOption.trim().split("[=]", 2); String sourceName = parts[0].trim(); String targetName = parts[1].trim(); Module source = moduleLayer.findModule(sourceName).orElse(null); Module target = moduleLayer.findModule(targetName).orElse(null); if (source == null || target == null) { logModuleNotFound(readsOption, source == null ? sourceName : targetName); continue; } try { // When the source module is inside the application layer controller.addReads(source, target); } catch (IllegalArgumentException ignored) { // When the source module is in the boot layer source.addReads(target); } } } private static void addExportsOrOpens(String property, ModuleLayer moduleLayer, ModuleLayer.Controller controller, boolean opens) { for (String option : property.trim().split(",")) { // module.source/package=module.target String[] parts = option.trim().split("[/=]", 3); String sourceName = parts[0].trim(); String aPackage = parts[1].trim(); String targetName = parts[2].trim(); Module source = moduleLayer.findModule(sourceName).orElse(null); Module target = moduleLayer.findModule(targetName).orElse(null); if (source == null || target == null) { logModuleNotFound(option, source == null ? sourceName : targetName); continue; } try { // When the source module is inside the application layer if (opens) { controller.addOpens(source, aPackage, target); } else { controller.addExports(source, aPackage, target); } } catch (IllegalArgumentException ignored) { // When the source module is in the boot layer if (opens) { source.addOpens(aPackage, target); } else { source.addExports(aPackage, target); } } } } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultModuleReader.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.resource.api.Resource; import java.io.IOException; import java.io.InputStream; import java.lang.module.ModuleReader; import java.net.URI; import java.net.URL; import java.util.Optional; import java.util.stream.Stream; /** * Provides an implementation of {@link ModuleReader} to work * with {@link Resource} * @author Thiago Henrique Hupner */ public class DefaultModuleReader implements ModuleReader { /** * Stores the resources */ private final Resource resource; /** * Constructor * @param resource the resource */ public DefaultModuleReader(Resource resource) { this.resource = resource; } @Override public Optional find(String name) throws IOException { try { return Optional.of(resource.getResource(name).toURI()); } catch (Exception e) { return Optional.empty(); } } @Override public Optional open(String name) throws IOException { // It was needed to override because the default implementation // opens the URL from an URI, so it loses its StreamHandler // and tries to load any resource from the GlobalArchiveStreamHandler URL url = resource.getResource(name); if (url != null) return Optional.of(url.openStream()); return Optional.empty(); } @Override public Stream list() throws IOException { return resource.getAllLocations().map(x -> x.startsWith("/") ? x.substring(1) : x); } @Override public void close() throws IOException { // nothing to do here. } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultModuleReference.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.resource.api.Resource; import java.io.IOException; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleReader; import java.lang.module.ModuleReference; import java.net.URI; /** * Provides an implementation of {@link ModuleReference} to work * with {@link Resource} * * @author Thiago Henrique Hupner */ public class DefaultModuleReference extends ModuleReference { /** * Stores the resource */ private final Resource resource; /** * Constructor * * @param descriptor the descriptor * @param location the location * @param resource the resource */ public DefaultModuleReference(ModuleDescriptor descriptor, URI location, Resource resource) { super(descriptor, location); this.resource = resource; } @Override public ModuleReader open() throws IOException { return new DefaultModuleReader(resource); } Resource getResource() { return resource; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultMultiPartManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.MultiPartManager; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationRequest; import jakarta.servlet.ServletException; import jakarta.servlet.http.Part; import java.util.Collection; import java.util.Collections; /** * The default MultiPartManager. * *

* This MultiPartManager is a no-op. *

* * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultMultiPartManager implements MultiPartManager { /** * Constructor. */ public DefaultMultiPartManager() { } @Override public Collection getParts(WebApplication webApplication, WebApplicationRequest request) throws ServletException { return Collections.emptyList(); } @Override public Part getPart(WebApplication webApplication, WebApplicationRequest request, String name) throws ServletException { return null; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultNamedRequestDispatcher.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.ServletEnvironment; import cloud.piranha.core.api.WebApplicationRequest; import cloud.piranha.core.api.WebApplicationResponse; import jakarta.servlet.DispatcherType; import static jakarta.servlet.DispatcherType.FORWARD; import static jakarta.servlet.DispatcherType.INCLUDE; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletRequestWrapper; import jakarta.servlet.ServletResponse; import jakarta.servlet.ServletResponseWrapper; import java.io.IOException; import java.io.OutputStream; /** * The default named RequestDispatcher. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultNamedRequestDispatcher implements RequestDispatcher { /** * Stores the Servlet environment. */ private final ServletEnvironment environment; /** * Constructor. * * @param environment the Servlet environment. */ public DefaultNamedRequestDispatcher(ServletEnvironment environment) { this.environment = environment; } @Override public void forward(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { WebApplicationRequest request = unwrap(servletRequest); WebApplicationResponse response = unwrap(servletResponse); /* - JAVADOC * * If the response is already committed throw an IllegalStateException. */ if (servletResponse.isCommitted()) { throw new IllegalStateException("Response has already been committed"); } /* - JAVADOC, Servlet:SPEC:77 * * Clear uncommitted output in the response buffer before forwarding */ servletResponse.resetBuffer(); /* - JAVADOC * * Sets the dispatcher type of the given request to DispatcherType.FORWARD. */ DispatcherType dispatcherType = request.getDispatcherType(); request.setDispatcherType(FORWARD); environment.getServlet().service(servletRequest, servletResponse); /* - Servlet:SPEC:80 * * If the request has not entered async mode and the response is not yet * committed we need to flush the response and close the outputstream. */ if (!servletRequest.isAsyncStarted() && !servletResponse.isCommitted()) { servletResponse.flushBuffer(); try ( OutputStream outputStream = response.getWebApplicationOutputStream()) { outputStream.flush(); } request.setDispatcherType(dispatcherType); } } @Override public void include(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { WebApplicationRequest request = unwrap(servletRequest); /* - JAVADOC, Servlet:SPEC:192 * * Set dispatcher type to DispatcherType.INCLUDE. */ DispatcherType dispatcherType = request.getDispatcherType(); request.setDispatcherType(INCLUDE); environment.getServlet().service(servletRequest, servletResponse); request.setDispatcherType(dispatcherType); } /** * Unwrap the servlet request. * * @param servletRequest the servlet request. * @return the web application request. */ private WebApplicationRequest unwrap(ServletRequest servletRequest) { ServletRequest currentRequest = servletRequest; while (currentRequest instanceof ServletRequestWrapper wrapper) { currentRequest = wrapper.getRequest(); } return (WebApplicationRequest) currentRequest; } /** * Unwrap the servlet response. * * @param servletResponse the servlet response. * @return the web application response. */ private WebApplicationResponse unwrap(ServletResponse servletResponse) { ServletResponse currentResponse = servletResponse; while (currentResponse instanceof ServletResponseWrapper wrapper) { currentResponse = wrapper.getResponse(); } return (WebApplicationResponse) currentResponse; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultObjectInstanceManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.ObjectInstanceManager; import jakarta.servlet.Filter; import jakarta.servlet.Servlet; import jakarta.servlet.ServletException; import java.util.EventListener; /** * The default object instance manager. * *

* This object instance manager does not do any injection and that is by design. * If you need injection into Servlets use the appropriate object instance * manager for your injection framework. *

* * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultObjectInstanceManager implements ObjectInstanceManager { /** * Constructor. */ public DefaultObjectInstanceManager() { } /** * Create the filter. * * @param the return type. * @param filterClass the filter class. * @return the filter. * @throws ServletException when it fails to create the filter. */ @Override public T createFilter(Class filterClass) throws ServletException { T result = null; try { result = filterClass.getDeclaredConstructor().newInstance(); } catch (Exception exception) { throw new ServletException("Unable to create filter", exception); } return result; } /** * Create the listener. * * @param the type. * @param clazz the class of the listener to create. * @return the listener. * @throws ServletException when it fails to create the listener. */ @Override public T createListener(Class clazz) throws ServletException { T result; try { result = clazz.getDeclaredConstructor().newInstance(); } catch (Exception exception) { throw new ServletException("Unable to create listener", exception); } return result; } /** * Create the servlet. * * @param the return type. * @param servletClass the servlet class. * @return the servlet. * @throws ServletException when it fails to create the servlet. */ @Override public T createServlet(Class servletClass) throws ServletException { T result; try { result = servletClass.getDeclaredConstructor().newInstance(); } catch (Exception exception) { throw new ServletException("Unable to create servlet", exception); } return result; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultPiranha.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.Piranha; import cloud.piranha.core.api.PiranhaConfiguration; /** * The default Piranha implementation. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultPiranha implements Piranha { /** * Stores the configuration. */ protected PiranhaConfiguration configuration; /** * Constructor. */ public DefaultPiranha() { configuration = new DefaultPiranhaConfiguration(); } @Override public PiranhaConfiguration getConfiguration() { return configuration; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultPiranhaBuilder.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.PiranhaBuilder; import java.lang.System.Logger; import static java.lang.System.Logger.Level.WARNING; import java.lang.reflect.InvocationTargetException; /** * The default PiranhaBuilder. * * @author Manfred Riem (mriem@manorrock.com) * @param the Piranha type. */ public class DefaultPiranhaBuilder implements PiranhaBuilder { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(DefaultPiranhaBuilder.class.getName()); /** * Stores the class type. */ private Class clazz; /** * Constructor. */ public DefaultPiranhaBuilder() { } @Override public T build() { try { return (T) clazz.getDeclaredConstructor().newInstance(); } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { LOGGER.log(WARNING, "Unable to create Pirana instance", ex); return null; } } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultPiranhaConfiguration.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.PiranhaConfiguration; import java.io.File; import java.util.HashMap; import java.util.Map; /** * The default Piranha configuration implementation. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultPiranhaConfiguration implements PiranhaConfiguration { /** * Stores the configuration. */ private final Map configuration = new HashMap<>(); /** * Constructor */ public DefaultPiranhaConfiguration() { } @Override public boolean getBoolean(String key, boolean defaultValue) { if (configuration.containsKey(key)) { return (boolean) configuration.get(key); } return defaultValue; } @Override public Class getClass(String key) { return (Class) configuration.get(key); } @Override public File getFile(String key) { return (File) configuration.get(key); } @Override public Integer getInteger(String key) { return (Integer) configuration.get(key); } @Override public Long getLong(String key) { return (Long) configuration.get(key); } @Override public String getString(String key) { return (String) configuration.get(key); } @Override public void setBoolean(String key, Boolean value) { configuration.put(key, value); } @Override public void setClass(String key, Class value) { configuration.put(key, value); } @Override public void setFile(String key, File value) { configuration.put(key, value); } @Override public void setInteger(String key, Integer value) { configuration.put(key, value); } @Override public void setLong(String key, Long value) { configuration.put(key, value); } @Override public void setString(String key, String value) { configuration.put(key, value); } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultPushBuilder.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplicationRequest; import jakarta.servlet.http.PushBuilder; import java.util.HashSet; import java.util.Set; /** * The default PushBuilder. * * @author Manfred Riem (mriem@manorrock.com) */ @SuppressWarnings("deprecation") public class DefaultPushBuilder implements PushBuilder { /** * Stores the header manager. */ private final DefaultHttpHeaderManager headerManager = new DefaultHttpHeaderManager(); /** * Stores the method. */ private String method; /** * Stores the path. */ private String path; /** * Stores the query string. */ private String queryString; /** * Stores the web application request. */ @SuppressWarnings("unused") private final WebApplicationRequest request; /** * Stores the session id. */ private String sessionId; /** * Constructor. * * @param request the web application request. */ public DefaultPushBuilder(WebApplicationRequest request) { this.request = request; } @Override public PushBuilder method(String method) { this.method = method; return this; } @Override public PushBuilder queryString(String queryString) { this.queryString = queryString; return this; } @Override public PushBuilder sessionId(String sessionId) { this.sessionId = sessionId; return this; } @Override public PushBuilder setHeader(String name, String value) { headerManager.setHeader(name, value); return this; } @Override public PushBuilder addHeader(String name, String value) { headerManager.addHeader(name, value); return this; } @Override public PushBuilder removeHeader(String name) { headerManager.removeHeader(name); return this; } @Override public PushBuilder path(String path) { this.path = path; return this; } @Override public void push() { path = null; } @Override public String getMethod() { return method; } @Override public String getQueryString() { return queryString; } @Override public String getSessionId() { return sessionId; } @Override public Set getHeaderNames() { HashSet names = new HashSet<>(); headerManager.getHeaderNames() .asIterator() .forEachRemaining(name -> names.add(name)); return names; } @Override public String getHeader(String name) { return headerManager.getHeader(name); } @Override public String getPath() { return path; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultSecurityManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.SecurityConstraint; import cloud.piranha.core.api.SecurityRoleReference; import cloud.piranha.core.api.WebApplication; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * The default SecurityManager. * *

* This SecurityManager is securing nothing. *

* * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultSecurityManager implements cloud.piranha.core.api.SecurityManager { /** * Stores the security constraints. */ private List securityConstraints; /** * Stores the security role references. */ private Map> securityRoleReferences; /** * Constructor. */ public DefaultSecurityManager() { securityConstraints = new ArrayList<>(); securityRoleReferences = new HashMap<>(); } @Override public boolean authenticate(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { return false; } @Override public void declareRoles(String[] roles) { } @Override public Set getRoles() { return Collections.emptySet(); } @Override public List getSecurityConstraints() { return securityConstraints; } @Override public Map> getSecurityRoleReferences() { return securityRoleReferences; } @Override public WebApplication getWebApplication() { return null; } @Override public boolean isUserInRole(HttpServletRequest request, String role) { return false; } @Override public void login(HttpServletRequest request, String username, String password) throws ServletException { throw new ServletException("Unable to login"); } @Override public void logout(HttpServletRequest request, HttpServletResponse response) throws ServletException { } @Override public void setSecurityConstraints(List securityConstraints) { this.securityConstraints = securityConstraints; } @Override public void setSecurityRoleReferences(Map> securityRoleReferences) { this.securityRoleReferences = securityRoleReferences; } @Override public void setWebApplication(WebApplication webApplication) { } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import jakarta.servlet.DispatcherType; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import static jakarta.servlet.http.HttpServletResponse.SC_NOT_FOUND; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; /** * The default Servlet. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultServlet extends HttpServlet { private static final long serialVersionUID = 1331822806510796938L; /** * Constructor. */ public DefaultServlet() { } /** * Get the requested resource. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. * @throws ServletException when a Servlet error occurs. */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { InputStream resource = getResource(request); if (resource == null) { response.sendError(SC_NOT_FOUND); return; } setContentType(request, response); try (InputStream inputStream = new BufferedInputStream(resource)) { try (OutputStream outputStream = new BufferedOutputStream(response.getOutputStream())) { inputStream.transferTo(outputStream); outputStream.flush(); } catch (IllegalStateException ise) { try (PrintWriter writer = response.getWriter()) { writer.print(new String(inputStream.readAllBytes())); writer.flush(); } } } } private InputStream getResource(HttpServletRequest request) { if (request.getServletContext() == null || request.getServletPath() == null) { return null; } return request.getServletContext().getResourceAsStream(getPath(request)); } private String getPath(HttpServletRequest request) { String requestURI; String contextPath; if (request.getDispatcherType() == DispatcherType.INCLUDE) { requestURI = (String) request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI); contextPath = (String) request.getAttribute(RequestDispatcher.INCLUDE_CONTEXT_PATH); } else { requestURI = request.getRequestURI(); contextPath = request.getContextPath(); } return requestURI.substring(contextPath.length()); } private void setContentType(HttpServletRequest request, HttpServletResponse response) { String uri = request.getRequestURI(); String filename = uri.contains("/") ? uri.substring(uri.lastIndexOf("/") + 1) : uri.isEmpty()? "" : uri.substring(1); String mimeType = request.getServletContext().getMimeType(filename); if (mimeType != null) { response.setContentType(mimeType); } else { response.setContentType("application/octet-stream"); } } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultServletConnection.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import jakarta.servlet.ServletConnection; /** * The default ServletConnection. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultServletConnection implements ServletConnection { /** * Constructor. */ public DefaultServletConnection() { } @Override public String getConnectionId() { throw new UnsupportedOperationException("Not supported yet."); } @Override public String getProtocol() { throw new UnsupportedOperationException("Not supported yet."); } @Override public String getProtocolConnectionId() { throw new UnsupportedOperationException("Not supported yet."); } @Override public boolean isSecure() { throw new UnsupportedOperationException("Not supported yet."); } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultServletEnvironment.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.ServletEnvironment; import cloud.piranha.core.api.WebApplication; import jakarta.servlet.MultipartConfigElement; import jakarta.servlet.Servlet; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletSecurityElement; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * The default ServletEnvironment. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultServletEnvironment implements ServletEnvironment { /** * Stores the async supported flag. */ protected boolean asyncSupported; /** * Stores the class name. */ protected String className; /** * Stores the init parameters. */ protected final Map initParameters; /** * Stores the load on startup value. */ protected int loadOnStartup; /** * Stores the multi-part config. */ protected MultipartConfigElement multipartConfig; /** * Stores the run-as-role. */ protected String runAsRole; /** * Stores the servlet. */ protected Servlet servlet; /** * Stores the servlet class. */ protected Class servletClass; /** * Stores the servlet name. */ protected final String servletName; /** * Stores the status. */ protected int status; /** * Stores the unavailableException. */ protected Throwable unavailableException; /** * Stores the web application. */ protected final WebApplication webApp; /** * Constructor. * * @param webApp the web application. * @param servletName the servlet name. */ public DefaultServletEnvironment(DefaultWebApplication webApp, String servletName) { this.asyncSupported = false; this.initParameters = new ConcurrentHashMap<>(1); this.loadOnStartup = -1; this.servletName = servletName; this.webApp = webApp; } /** * Constructor. * * @param webApp the web application. * @param servletName the servlet name. * @param servlet the servlet. */ public DefaultServletEnvironment(DefaultWebApplication webApp, String servletName, Servlet servlet) { this(webApp, servletName); this.className = servlet.getClass().getName(); this.servlet = servlet; } @Override public Set addMapping(String... urlPatterns) { return webApp.addServletMapping(servletName, urlPatterns); } @Override public String getClassName() { return className; } @Override public String getInitParameter(String name) { return initParameters.get(name); } @Override public Enumeration getInitParameterNames() { return Collections.enumeration(initParameters.keySet()); } @Override public Map getInitParameters() { return initParameters; } @Override public int getLoadOnStartup() { return loadOnStartup; } @Override public Collection getMappings() { Collection result = new ArrayList<>(); Collection mappings = webApp.getMappings(servletName); if (mappings != null) { result.addAll(mappings); } return result; } @Override public MultipartConfigElement getMultipartConfig() { return multipartConfig; } @Override public String getName() { return servletName; } @Override public String getRunAsRole() { return runAsRole; } @Override public Servlet getServlet() { return servlet; } @Override public ServletContext getServletContext() { return webApp; } @Override public Class getServletClass() { return servletClass; } @Override public String getServletName() { return servletName; } @Override public int getStatus() { return status; } @Override public WebApplication getWebApplication() { return this.webApp; } @Override public boolean isAsyncSupported() { return asyncSupported; } @Override public void setAsyncSupported(boolean asyncSupported) { this.asyncSupported = asyncSupported; } @Override public void setClassName(String className) { this.className = className; } @Override public boolean setInitParameter(String name, String value) { boolean result = false; if (!initParameters.containsKey(name)) { initParameters.put(name, value); result = true; } return result; } @Override public Set setInitParameters(Map initParameters) { HashSet conflicting = new HashSet<>(); if (initParameters != null) { initParameters.entrySet().forEach(entry -> { String name = entry.getKey(); String value = entry.getValue(); if (name == null) { throw new IllegalArgumentException("A null name is not allowed"); } if (value == null) { throw new IllegalArgumentException("A null value is not allowed"); } if (!setInitParameter(name, value)) { conflicting.add(name); } }); } return conflicting; } @Override public void setLoadOnStartup(int loadOnStartup) { this.loadOnStartup = loadOnStartup; } @Override public void setMultipartConfig(MultipartConfigElement multipartConfig) { this.multipartConfig = multipartConfig; } @Override public void setRunAsRole(String runAsRole) { this.runAsRole = runAsRole; } @Override public void setServlet(Servlet servlet) { this.servlet = servlet; } @Override public Set setServletSecurity(ServletSecurityElement servletSecurityElement) { return new HashSet<>(); } @Override public void setStatus(int status) { this.status = status; } @Override public Throwable getUnavailableException() { return unavailableException; } @Override public void setUnavailableException(Throwable unavailableException) { this.unavailableException = unavailableException; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultServletInvocation.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.FilterEnvironment; import cloud.piranha.core.api.ServletEnvironment; import cloud.piranha.core.api.ServletInvocation; import cloud.piranha.core.api.WebApplicationRequestMapping; import jakarta.servlet.FilterChain; import java.util.List; /** * The default servlet invocation * * @author Arjan Tijms * */ public class DefaultServletInvocation implements ServletInvocation { /** * Stores the invocation path. */ private String invocationPath; /** * Stores the servlet name. */ private String servletName; /** * Stores the servlet path. */ private String servletPath; /** * The original servlet path that was requested. * This is used if the invocation locator tried alternative * servlet paths, such as with welcome files. * */ private String originalServletPath; /** * Stores the path info. */ private String pathInfo; /** * Stores the web application request mapping. */ private WebApplicationRequestMapping applicationRequestMapping; /** * Stores the DefaultHttpServletMapping */ private DefaultHttpServletMapping httpServletMapping = new DefaultHttpServletMapping(); /** * Stores the servlet environment. */ private ServletEnvironment servletEnvironment; /** * Stores the filter environment. */ private List filterEnvironments; /** * Stores the filter chain. */ private FilterChain filterChain; /** * Boolean to indicate whether this invocation results * from a getNamedDispatcher */ private boolean fromNamed; /** * Constructor. */ public DefaultServletInvocation() { } @Override public String getInvocationPath() { return invocationPath; } /** * Set the invocation path. * * @param invocationPath the invocation path. */ public void setInvocationPath(String invocationPath) { this.invocationPath = invocationPath; } @Override public String getServletName() { return servletName; } /** * Set the servlet name. * * @param servletName the servlet name. */ public void setServletName(String servletName) { this.servletName = servletName; } @Override public String getServletPath() { return servletPath; } /** * Set the servlet path. * * @param servletPath the servlet path. */ public void setServletPath(String servletPath) { this.servletPath = servletPath; } @Override public String getOriginalServletPath() { return originalServletPath; } /** * Set the originalServletPath * * @param originalServletPath the originalServletPath */ public void setOriginalServletPath(String originalServletPath) { this.originalServletPath = originalServletPath; } @Override public String getPathInfo() { return pathInfo; } /** * Set the path info. * * @param pathInfo the path info. */ public void setPathInfo(String pathInfo) { this.pathInfo = pathInfo; } @Override public WebApplicationRequestMapping getApplicationRequestMapping() { return applicationRequestMapping; } /** * Set the web application request mapping. * * @param applicationRequestMapping the web application request mapping. */ public void setApplicationRequestMapping(WebApplicationRequestMapping applicationRequestMapping) { this.applicationRequestMapping = applicationRequestMapping; } /** * Get the HTTP servlet mapping. * * @return the httpServletMapping */ public DefaultHttpServletMapping getHttpServletMapping() { return httpServletMapping; } @Override public ServletEnvironment getServletEnvironment() { return servletEnvironment; } /** * Set the servlet environment. * * @param servletEnvironment the servlet environment. */ public void setServletEnvironment(ServletEnvironment servletEnvironment) { this.servletEnvironment = servletEnvironment; } @Override public List getFilterEnvironments() { return filterEnvironments; } /** * Set the filter environments. * * @param filterEnvironments the filter environments. */ public void setFilterEnvironments(List filterEnvironments) { this.filterEnvironments = filterEnvironments; } @Override public FilterChain getFilterChain() { return filterChain; } /** * Set the filter chain. * * @param filterChain the filter chain. */ public void setFilterChain(FilterChain filterChain) { this.filterChain = filterChain; } @Override public boolean isFromNamed() { return fromNamed; } @Override public void setFromNamed(boolean fromNamed) { this.fromNamed = fromNamed; } /** * Seed the filter chain. */ public void seedFilterChain() { setFilterChain( new DefaultFilterChain( this, getServletEnvironment() == null ? null : getServletEnvironment().getServlet())); } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultServletRequestDispatcher.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletRequestWrapper; import jakarta.servlet.ServletResponse; import jakarta.servlet.UnavailableException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequestWrapper; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import java.io.IOException; import java.io.PrintStream; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.Predicate; import cloud.piranha.core.api.CurrentRequestHolder; import cloud.piranha.core.api.FilterEnvironment; import cloud.piranha.core.api.ServletEnvironment; import cloud.piranha.core.api.WebApplicationRequest; import static cloud.piranha.core.api.CurrentRequestHolder.CURRENT_REQUEST_ATTRIBUTE; import static cloud.piranha.core.impl.DefaultWebApplicationRequest.unwrap; import static jakarta.servlet.AsyncContext.ASYNC_CONTEXT_PATH; import static jakarta.servlet.AsyncContext.ASYNC_PATH_INFO; import static jakarta.servlet.AsyncContext.ASYNC_QUERY_STRING; import static jakarta.servlet.AsyncContext.ASYNC_REQUEST_URI; import static jakarta.servlet.AsyncContext.ASYNC_SERVLET_PATH; import static jakarta.servlet.DispatcherType.ASYNC; import static jakarta.servlet.DispatcherType.ERROR; import static jakarta.servlet.DispatcherType.FORWARD; import static jakarta.servlet.DispatcherType.INCLUDE; import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; import static jakarta.servlet.http.HttpServletResponse.SC_NOT_FOUND; /** * The default ServletRequestDispatcher. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultServletRequestDispatcher implements RequestDispatcher { /** * Stores the previous request attribute name */ static final String PREVIOUS_REQUEST = "piranha.previous.request"; /** * Stores the async attributes. */ private static final List ASYNC_ATTRIBUTES = Arrays.asList( ASYNC_CONTEXT_PATH, ASYNC_PATH_INFO, ASYNC_QUERY_STRING, ASYNC_REQUEST_URI, ASYNC_SERVLET_PATH); /** * Stores the servlet invocation. */ private final DefaultServletInvocation servletInvocation; /** * The servletEnvironment corresponding to the target resource to which this * dispatcher forwards or includes. * *

* It contains the actual Servlet, to process the forwarded or included * request, as well as meta data for this Servlet. */ private final ServletEnvironment servletEnvironment; /** * Stores the path. */ private final String path; /** * Stores the invocation finder. */ private final DefaultInvocationFinder invocationFinder; /** * Stores the web application. */ private final DefaultWebApplication webApplication; /** * Constructor. * * @param servletInvocation The servlet invocation containing all info this * dispatcher uses to dispatch to the contained Servlet. * @param webApplication the web application. */ public DefaultServletRequestDispatcher(DefaultServletInvocation servletInvocation, DefaultWebApplication webApplication) { this.servletInvocation = servletInvocation; this.webApplication = webApplication; this.invocationFinder = webApplication.invocationFinder; this.servletEnvironment = servletInvocation == null ? null : servletInvocation.getServletEnvironment(); this.path = servletInvocation == null ? null : servletInvocation.getInvocationPath(); } /** * Dispatches using the REQUEST dispatch type * * @param webappRequest the request. * @param webappResponse the response. * @throws ServletException when a servlet error occurs. * @throws IOException when an I/O error occurs. */ public void request(DefaultWebApplicationRequest webappRequest, DefaultWebApplicationResponse webappResponse) throws ServletException, IOException { Throwable exception = null; if (servletInvocation == null || !servletInvocation.canInvoke() && !servletInvocation.isServletUnavailable()) { // If there's nothing to invoke at all, there was nothing found, so return a 404 webappResponse.sendError(404); } else { // There's either a Servlet, Filter or both found matching the request. try { if (servletInvocation.getServletEnvironment() != null) { webappRequest.setAsyncSupported(isAsyncSupportedInChain()); } webappRequest.setMultipartConfig(servletEnvironment.getMultipartConfig()); webappRequest.setServletPath(servletInvocation.getServletPath()); webappRequest.setOriginalServletPath(servletInvocation.getOriginalServletPath()); webappRequest.setPathInfo(servletInvocation.getPathInfo()); webappRequest.setHttpServletMapping(servletInvocation.getHttpServletMapping()); servletInvocation.getFilterChain().doFilter(webappRequest, webappResponse); } catch (Exception e) { if (webappRequest.getAttribute("piranha.request.exception") != null) { exception = (Exception) webappRequest.getAttribute("piranha.request.exception"); } else { exception = e; } } } /* * REFACTOR - We used a response header to signal that we are not * listening to add/setHeader. In the block below we remove the header * and reset the buffer as we need the code below do its work. However * we really need to refactor this and move the code below to the * DefaultWebApplicationResponse, because it should be handled in the * sendError call. */ if (webappResponse.getHeader("sendErrorCalled") != null) { webappResponse.headerManager.removeHeader("sendErrorCalled"); if (!webappResponse.isCommitted()) { webappResponse.resetBuffer(); } } if (exception != null) { webappResponse.setStatus(exception instanceof UnavailableException ? SC_NOT_FOUND : SC_INTERNAL_SERVER_ERROR); } String errorPagePath = webApplication.getManager().getErrorPageManager() != null ? webApplication.getManager().getErrorPageManager().getErrorPage(exception, webappResponse) : null; if (errorPagePath != null) { try { webApplication.getRequestDispatcher(errorPagePath).error(servletInvocation == null ? null : servletInvocation.getServletName(), webappRequest, webappResponse, exception); } catch (Exception e) { rethrow(e); } } else if (exception != null) { if (webappResponse.isWriterAcquired()) { exception.printStackTrace(webappResponse.getWriter()); } else { exception.printStackTrace(new PrintStream(webappResponse.getOutputStream())); } webappResponse.flushBuffer(); rethrow(exception); } else if (webappRequest.getAttribute(ERROR_MESSAGE) != null) { // Specified by spec/javadoc: "The server defaults to creating the response to look like an HTML-formatted server error page containing the specified message, // setting the content type to "text/html"." webappResponse.setContentType("text/html"); webappResponse.getWriter() .write("" + webappRequest.getAttribute(ERROR_MESSAGE) + ""); } if (!webappRequest.isAsyncStarted()) { webappResponse.flushBuffer(); } } /** * Dispatches using the FORWARD or ASYNC dispatch type - Forward the request * and response. * * @param servletRequest the request. * @param servletResponse the response. * @throws ServletException when a servlet error occurs. * @throws IOException when an I/O error occurs. */ @Override public void forward(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { if (servletRequest.getDispatcherType().equals(ASYNC)) { // Asynchronous forward asyncForward(servletRequest, servletResponse); return; } // Regular (synchronous) forward dispatchSyncForward(servletRequest, servletResponse); } /** * Dispatches using the INCLUDE dispatch type - Include the request and * response. * * @param servletRequest the request. * @param servletResponse the response. * @throws ServletException when a servlet error occurs. * @throws IOException when an I/O error occurs. */ @Override public void include(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { /* * We only do includes for a HttpServletRequest/Response pair. */ if (!(servletRequest instanceof HttpServletRequest)) { throw new ServletException("Request is not a HttpServletRequest"); } if (!(servletResponse instanceof HttpServletResponse)) { throw new ServletException("Response is not a HttpServletResponse"); } /* * Unwrap the passed ServletRequest to the underlying WebApplicationRequest. */ WebApplicationRequest request = unwrap(servletRequest, WebApplicationRequest.class); /** * Set the include attributes. */ request.setAttribute(INCLUDE_CONTEXT_PATH, request.getContextPath()); request.setAttribute(INCLUDE_PATH_INFO, request.getPathInfo()); request.setAttribute(INCLUDE_QUERY_STRING, request.getQueryString()); request.setAttribute(INCLUDE_REQUEST_URI, request.getRequestURI()); request.setAttribute(INCLUDE_SERVLET_PATH, request.getServletPath()); /** * Set the new dispatcher type. */ request.setDispatcherType(INCLUDE); request.setServletPath(path == null ? "/" + servletEnvironment.getServletName() : getServletPath(path)); request.setPathInfo(null); if (path != null) { request.setQueryString(getQueryString(path)); } invocationFinder.addFilters(INCLUDE, servletInvocation, request.getServletPath(), ""); if (servletInvocation.getServletEnvironment() != null) { request.setAsyncSupported(request.isAsyncSupported() && isAsyncSupportedInChain()); } /* * Aggregate the request parameters with giving the new parameter values * precedence over the old ones. */ if (request.getQueryString() != null) { String queryString = request.getQueryString(); Map parameters = request.getModifiableParameterMap(); for (String param : queryString.split("&")) { String[] pair = param.split("="); String key = URLDecoder.decode(pair[0], StandardCharsets.UTF_8); String value = ""; if (pair.length > 1) { value = URLDecoder.decode(pair[1], StandardCharsets.UTF_8); } String[] values = parameters.get(key); if (values == null) { values = new String[]{value}; parameters.put(key, values); } else { String[] newValues = new String[values.length + 1]; System.arraycopy(values, 0, newValues, 1, values.length); newValues[0] = value; parameters.put(key, newValues); } } } /* * Execute the filter chain. */ try { servletEnvironment.getWebApplication().linkRequestAndResponse(servletRequest, servletResponse); servletInvocation.getFilterChain().doFilter(servletRequest, servletResponse); servletEnvironment.getWebApplication().unlinkRequestAndResponse(servletRequest, servletResponse); } catch (Exception e) { rethrow(e); } finally { /* * Set servlet path and path info back to original values. */ request.setServletPath((String) request.getAttribute(INCLUDE_SERVLET_PATH)); request.setPathInfo((String) request.getAttribute(INCLUDE_PATH_INFO)); /* * Remove include attributes. */ request.removeAttribute(INCLUDE_CONTEXT_PATH); request.removeAttribute(INCLUDE_PATH_INFO); request.removeAttribute(INCLUDE_QUERY_STRING); request.removeAttribute(INCLUDE_REQUEST_URI); request.removeAttribute(INCLUDE_SERVLET_PATH); } } /** * Send an error response. * * @param servletName the servlet name. * @param servletRequest the servlet request. * @param servletResponse the servlet response. * @param throwable the throwable. * @throws Exception when a serious error occurs. */ public void error(String servletName, ServletRequest servletRequest, ServletResponse servletResponse, Throwable throwable) throws Exception { DefaultWebApplicationRequest errorRequest = new DefaultWebApplicationRequest(); HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; response.resetBuffer(); errorRequest.setWebApplication(servletEnvironment.getWebApplication()); errorRequest.setMultipartConfig(servletEnvironment.getMultipartConfig()); errorRequest.setContextPath(request.getContextPath()); errorRequest.setDispatcherType(ERROR); HttpSession session = request.getSession(false); if (session != null) { errorRequest.setCurrentSessionId(session.getId()); } if (path != null) { setForwardAttributes(request, errorRequest, FORWARD_CONTEXT_PATH, FORWARD_PATH_INFO, FORWARD_QUERY_STRING, FORWARD_REQUEST_URI, FORWARD_SERVLET_PATH); errorRequest.setServletPath(getServletPath(path)); errorRequest.setQueryString(getQueryString(path)); } else { errorRequest.setServletPath("/" + servletEnvironment.getServletName()); } if (throwable instanceof ServletException servletException) { errorRequest.setAttribute(ERROR_EXCEPTION, servletException.getRootCause() == null ? servletException : servletException.getRootCause()); errorRequest.setAttribute(ERROR_EXCEPTION_TYPE, servletException.getRootCause() == null ? servletException.getClass() : servletException.getRootCause().getClass()); errorRequest.setAttribute(ERROR_MESSAGE, servletException.getMessage()); } else { errorRequest.setAttribute(ERROR_EXCEPTION, throwable); errorRequest.setAttribute(ERROR_EXCEPTION_TYPE, throwable == null ? null : throwable.getClass()); errorRequest.setAttribute(ERROR_MESSAGE, throwable == null ? null : throwable.getMessage()); } errorRequest.setAttribute(ERROR_STATUS_CODE, response.getStatus()); errorRequest.setAttribute(ERROR_REQUEST_URI, request.getRequestURI()); errorRequest.setAttribute(ERROR_SERVLET_NAME, servletName); CurrentRequestHolder currentRequestHolder = updateCurrentRequest(request, errorRequest); copyAttributesFromRequest(request, errorRequest, attribute -> true); invocationFinder.addFilters(ERROR, servletInvocation, errorRequest.getServletPath(), ""); if (servletInvocation.getServletEnvironment() != null) { errorRequest.setAsyncSupported(request.isAsyncSupported() && isAsyncSupportedInChain()); } try { servletEnvironment.getWebApplication().linkRequestAndResponse(errorRequest, servletResponse); servletInvocation.getFilterChain().doFilter(errorRequest, servletResponse); servletEnvironment.getWebApplication().unlinkRequestAndResponse(errorRequest, servletResponse); } finally { restoreCurrentRequest(currentRequestHolder, request); } response.flushBuffer(); } /** * Checks if all filters (if any) and the servlet support async * * @return true if all supports, false otherwise */ private boolean isAsyncSupportedInChain() { List filterEnvironments = servletInvocation.getFilterEnvironments(); boolean hasFilterAsync = filterEnvironments == null || filterEnvironments.stream().allMatch(FilterEnvironment::isAsyncSupported); return servletInvocation.getServletEnvironment().isAsyncSupported() && hasFilterAsync; } private void setForwardAttributes(HttpServletRequest originalRequest, HttpServletRequest forwardedRequest, String... dispatcherKeys) { for (String dispatcherKey : dispatcherKeys) { setForwardAttribute(originalRequest, forwardedRequest, dispatcherKey); } } /** * Set forward attribute. * * @param originalRequest the original request * @param forwardedRequest the forward request. * @param dispatcherKey the dispatcher key. */ private void setForwardAttribute(HttpServletRequest originalRequest, HttpServletRequest forwardedRequest, String dispatcherKey) { String value = null; if (originalRequest.getAttribute(dispatcherKey) != null) { value = (String) originalRequest.getAttribute(dispatcherKey); } else { if (dispatcherKey.equals(FORWARD_CONTEXT_PATH)) { value = originalRequest.getContextPath(); } if (dispatcherKey.equals(FORWARD_PATH_INFO)) { value = originalRequest.getPathInfo(); } if (dispatcherKey.equals(FORWARD_QUERY_STRING)) { value = originalRequest.getQueryString(); } if (dispatcherKey.equals(FORWARD_REQUEST_URI)) { value = originalRequest.getRequestURI(); } if (dispatcherKey.equals(FORWARD_SERVLET_PATH)) { value = originalRequest.getServletPath(); } } forwardedRequest.setAttribute(dispatcherKey, value); } private CurrentRequestHolder updateCurrentRequest(HttpServletRequest originalRequest, HttpServletRequest forwardedRequest) { CurrentRequestHolder currentRequestHolder = (CurrentRequestHolder) originalRequest.getAttribute(CURRENT_REQUEST_ATTRIBUTE); if (currentRequestHolder != null) { currentRequestHolder.setRequest(forwardedRequest); forwardedRequest.setAttribute(CURRENT_REQUEST_ATTRIBUTE, currentRequestHolder); } forwardedRequest.setAttribute(PREVIOUS_REQUEST, originalRequest); return currentRequestHolder; } private void copyAttributesFromRequest(ServletRequest fromRequest, ServletRequest toRequest, Predicate attributesToExclude) { Collections.list(fromRequest.getAttributeNames()) .stream() .filter(attributesToExclude) .forEach(attributeName -> toRequest.setAttribute(attributeName, fromRequest.getAttribute(attributeName))); } private void restoreCurrentRequest(CurrentRequestHolder currentRequestHolder, HttpServletRequest originalRequest) { if (currentRequestHolder != null) { currentRequestHolder.setRequest(originalRequest); } } // #### ASYNC forward private methods private void asyncForward(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { if (servletRequest instanceof AsyncHttpDispatchWrapper || servletRequest instanceof AsyncNonHttpDispatchWrapper) { if (servletRequest instanceof AsyncHttpDispatchWrapper wrapper) { // The caller provided or let us default to an HttpServletRequest asyncHttpForward(wrapper, servletResponse); return; } // The caller provided a ServletRequest asyncNonHttpForward((AsyncNonHttpDispatchWrapper) servletRequest, servletResponse); } else { throw new IllegalStateException("Async invocations without wrapper not supported at this moment."); } } private void asyncHttpForward(AsyncHttpDispatchWrapper asyncHttpDispatchWrapper, ServletResponse servletResponse) throws ServletException, IOException { // A typical chain to arrive here is DefaultAsyncContext#dispatch -> DefaultAsyncDispatcher#dispatch -> forward -> asyncForwrd -> asyncHttpForward HttpServletRequest asyncStartRequest = asyncHttpDispatchWrapper.getRequest(); if (asyncStartRequest instanceof WebApplicationRequest) { // original request or previously dispatched request passed-in, not an application wrapped one // In this case our asyncHttpDispatchWrapper is both the object with which the Servlet will be invoked, as well as the // object on which the path and attributes for the previous path will be set. invokeTargetAsyncServlet(asyncHttpDispatchWrapper, servletResponse); } else if (asyncStartRequest instanceof HttpServletRequestWrapper applicationProvidedWrapper) { // Application wrapped request passed-in. We now need no make sure that the applications sees this request // We swap our asyncHttpDispatchWrapper from being the head of the chain, to be in between the request that was provided by the application // and the request it is wrapping. ServletRequest wrappedRequest = applicationProvidedWrapper.getRequest(); applicationProvidedWrapper.setRequest(asyncHttpDispatchWrapper); asyncHttpDispatchWrapper.setRequest(wrappedRequest); // Original chain: asyncHttpDispatchWrapper -> applicationProvidedWrapper (asyncStartRequest) -> wrappedRequest // New chain: applicationProvidedWrapper (asyncStartRequest) -> asyncHttpDispatchWrapper -> wrappedRequest invokeTargetAsyncServlet(applicationProvidedWrapper, asyncHttpDispatchWrapper, servletResponse); } else { throw new IllegalStateException("Async invocation with a request that was neither the original one nor a wrapped one: " + asyncStartRequest); } } private void asyncNonHttpForward(AsyncNonHttpDispatchWrapper asyncNonHttpDispatchWrapper, ServletResponse servletResponse) throws ServletException, IOException { // A typical chain to arrive here is DefaultAsyncContext#dispatch -> DefaultAsyncDispatcher#dispatch -> forward -> asyncForward -> asyncNonHttpForward ServletRequest asyncStartRequest = asyncNonHttpDispatchWrapper.getRequest(); if (asyncStartRequest instanceof ServletRequestWrapper applicationProvidedWrapper) { HttpServletRequest httpServletRequestInChain = findHttpServletRequestInChain(applicationProvidedWrapper); if (httpServletRequestInChain != null) { // We swap our asyncHttpDispatchWrapper from being the head of the chain, with a new wrapper, wrapping the HttpServletRequest that we found, and put // that in between the request that was provided by the application and the request it is wrapping. ServletRequest wrappedRequest = applicationProvidedWrapper.getRequest(); // Note that by doing this, methods called on HttpServletRequestWrapper itself (and not its super interface) will throw. AsyncHttpDispatchWrapper newAsyncHttpDispatchWrapper = new AsyncHttpDispatchWrapper((HttpServletRequest) wrappedRequest); applicationProvidedWrapper.setRequest(newAsyncHttpDispatchWrapper); // Original chain: asyncNonHttpDispatchWrapper -> applicationProvidedWrapper (asyncStartRequest) -> wrappedRequest -> .... -> HttpServletRequest // New chain: applicationProvidedWrapper (asyncStartRequest) -> newAsyncHttpDispatchWrapper -> wrappedRequest -> .... -> HttpServletRequest invokeTargetAsyncServlet(asyncStartRequest, httpServletRequestInChain, newAsyncHttpDispatchWrapper, servletResponse); } } } private void invokeTargetAsyncServlet(AsyncHttpDispatchWrapper asyncHttpDispatchWrapper, ServletResponse servletResponse) throws ServletException, IOException { invokeTargetAsyncServlet(asyncHttpDispatchWrapper, asyncHttpDispatchWrapper, servletResponse); } private void invokeTargetAsyncServlet(HttpServletRequest invokeServletRequest, AsyncHttpDispatchWrapper asyncHttpDispatchWrapper, ServletResponse servletResponse) throws ServletException, IOException { invokeTargetAsyncServlet(invokeServletRequest, invokeServletRequest, asyncHttpDispatchWrapper, servletResponse); } private void invokeTargetAsyncServlet(ServletRequest invokeServletRequest, HttpServletRequest previousPathRequest, AsyncHttpDispatchWrapper asyncHttpDispatchWrapper, ServletResponse servletResponse) throws ServletException, IOException { // A typical call chain to arrive here is DefaultAsyncContext#dispatch -> DefaultAsyncDispatcher#dispatch -> forward -> asyncForwrd -> asyncHttpForward -> invokeTargetAsyncServlet if (path != null) { setAsyncAttributes(previousPathRequest, asyncHttpDispatchWrapper); asyncHttpDispatchWrapper.setServletPath(getServletPath(path)); String queryString = getQueryString(path); if (queryString != null && !queryString.trim().equals("")) { asyncHttpDispatchWrapper.setQueryString(queryString); setRequestParameters(queryString, asyncHttpDispatchWrapper); } else { asyncHttpDispatchWrapper.setQueryString(previousPathRequest.getQueryString()); } asyncHttpDispatchWrapper.setRequestURI(previousPathRequest.getServletContext().getContextPath() + getServletPath(path)); asyncHttpDispatchWrapper.setAsWrapperAttribute(PREVIOUS_REQUEST, invokeServletRequest); } else { asyncHttpDispatchWrapper.setServletPath("/" + servletEnvironment.getServletName()); } servletEnvironment.getWebApplication().linkRequestAndResponse(invokeServletRequest, servletResponse); servletEnvironment.getServlet().service(invokeServletRequest, servletResponse); servletEnvironment.getWebApplication().unlinkRequestAndResponse(invokeServletRequest, servletResponse); } private void setAsyncAttributes(HttpServletRequest asyncStartRequest, AsyncHttpDispatchWrapper asyncHttpDispatchWrapper) { for (String asyncAttribute : ASYNC_ATTRIBUTES) { // Set the spec demanded attributes on asyncHttpDispatchWrapper with the values taken from asyncStartRequest setAsyncAttribute(asyncStartRequest, asyncHttpDispatchWrapper, asyncAttribute); } } private void setAsyncAttribute(HttpServletRequest originalRequest, AsyncHttpDispatchWrapper asyncHttpDispatchWrapper, String dispatcherKey) { String value = null; if (originalRequest.getAttribute(dispatcherKey) != null) { value = (String) originalRequest.getAttribute(dispatcherKey); } else { if (dispatcherKey.equals(ASYNC_CONTEXT_PATH)) { value = originalRequest.getContextPath(); } if (dispatcherKey.equals(ASYNC_PATH_INFO)) { value = originalRequest.getPathInfo(); } if (dispatcherKey.equals(ASYNC_QUERY_STRING)) { value = originalRequest.getQueryString(); } if (dispatcherKey.equals(ASYNC_REQUEST_URI)) { value = originalRequest.getRequestURI(); } if (dispatcherKey.equals(ASYNC_SERVLET_PATH)) { value = originalRequest.getServletPath(); } } asyncHttpDispatchWrapper.setAsWrapperAttribute(dispatcherKey, value); } private void setRequestParameters(String queryString, AsyncHttpDispatchWrapper asyncHttpDispatchWrapper) { Map parameters = asyncHttpDispatchWrapper.getWrapperParameters(); if (queryString != null) { for (String param : queryString.split("&")) { String[] pair = param.split("="); String key = URLDecoder.decode(pair[0], StandardCharsets.UTF_8); String value = ""; if (pair.length > 1) { value = URLDecoder.decode(pair[1], StandardCharsets.UTF_8); } String[] values = parameters.get(key); if (values == null) { values = new String[]{value}; parameters.put(key, values); } else { String[] newValues = new String[values.length + 1]; System.arraycopy(values, 0, newValues, 0, values.length); newValues[values.length] = value; parameters.put(key, newValues); } } } } private String getServletPath(String path) { return !path.contains("?") ? path : path.substring(0, path.indexOf("?")); } private String getQueryString(String path) { return !path.contains("?") ? null : path.substring(path.indexOf("?") + 1); } private HttpServletRequest findHttpServletRequestInChain(ServletRequest request) { ServletRequest currentRequest = request; while (currentRequest instanceof ServletRequestWrapper wrapper) { currentRequest = wrapper.getRequest(); if (currentRequest instanceof HttpServletRequest httpServletRequest) { return httpServletRequest; } } return null; } private void rethrow(Throwable exception) throws ServletException, IOException { if (exception instanceof ServletException servletException) { throw servletException; } if (exception instanceof IOException ioException) { throw ioException; } if (exception instanceof RuntimeException runtimeException) { throw runtimeException; } throw new IllegalStateException(exception); } /** * Execute a forward. * * @param servletRequest the Servlet request. * @param servletResponse the Servlet response. */ private void dispatchSyncForward( ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { /* * If response is already committed we need to throw an IllegalStateException. */ if (servletResponse.isCommitted()) { throw new IllegalStateException("Response already committed"); } /* * We only do forwards for a HttpServletRequest/Response pair. */ if (!(servletRequest instanceof HttpServletRequest)) { throw new ServletException("Request is not a HttpServletRequest"); } if (!(servletResponse instanceof HttpServletResponse)) { throw new ServletException("Response is not a HttpServletResponse"); } /* * Reset the buffer if we haven't committed yet. */ servletResponse.resetBuffer(); /* * Unwrap the passed ServletRequest to the underlying WebApplicationRequest. */ WebApplicationRequest request = unwrap(servletRequest, WebApplicationRequest.class); /* * Set the forward attributes. */ request.setAttribute(FORWARD_CONTEXT_PATH, request.getContextPath()); request.setAttribute(FORWARD_PATH_INFO, request.getPathInfo()); request.setAttribute(FORWARD_QUERY_STRING, request.getQueryString()); request.setAttribute(FORWARD_REQUEST_URI, request.getRequestURI()); request.setAttribute(FORWARD_SERVLET_PATH, request.getServletPath()); /* * Set the new dispatcher type. */ request.setDispatcherType(FORWARD); /* * Set the new servlet path and the new query string. */ if (path != null) { request.setServletPath(getServletPath(path)); request.setQueryString(getQueryString(path)); } else { request.setServletPath("/" + servletEnvironment.getServletName()); request.setQueryString(request.getQueryString()); } /* * Aggregate the request parameters with giving the new parameter values * precedence over the old ones. */ if (request.getQueryString() != null) { String queryString = request.getQueryString(); Map parameters = request.getModifiableParameterMap(); for (String param : queryString.split("&")) { String[] pair = param.split("="); String key = URLDecoder.decode(pair[0], StandardCharsets.UTF_8); String value = ""; if (pair.length > 1) { value = URLDecoder.decode(pair[1], StandardCharsets.UTF_8); } String[] values = parameters.get(key); if (values == null) { values = new String[]{value}; parameters.put(key, values); } else { String[] newValues = new String[values.length + 1]; System.arraycopy(values, 0, newValues, 1, values.length); newValues[0] = value; parameters.put(key, newValues); } } } invocationFinder.addFilters(FORWARD, servletInvocation, request.getServletPath(), ""); if (servletInvocation.getServletEnvironment() != null) { request.setAsyncSupported(request.isAsyncSupported() && isAsyncSupportedInChain()); } /* * Execute the filter chain. */ servletEnvironment.getWebApplication().linkRequestAndResponse(servletRequest, servletResponse); servletInvocation.getFilterChain().doFilter(servletRequest, servletResponse); servletEnvironment.getWebApplication().unlinkRequestAndResponse(servletRequest, servletResponse); /* * Flush the response buffer. */ servletResponse.flushBuffer(); } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultServletRequestManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.ServletRequestManager; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletRequestAttributeEvent; import jakarta.servlet.ServletRequestAttributeListener; import jakarta.servlet.ServletRequestEvent; import jakarta.servlet.ServletRequestListener; import jakarta.servlet.http.HttpServletRequest; import java.lang.System.Logger; import static java.lang.System.Logger.Level.TRACE; import java.util.ArrayList; import java.util.EventListener; import java.util.List; /** * The default servlet request manager. * * @author Arjan Tijms * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultServletRequestManager implements ServletRequestManager { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(DefaultServletRequestManager.class.getName()); /** * Stores the ServletRequestAttribute listeners. */ private final List attributeListeners = new ArrayList<>(); /** * Stores the ServletRequest listeners. */ private final List requestListeners = new ArrayList<>(); /** * Constructor. */ public DefaultServletRequestManager() { } @Override public void addListener(T listener) { if (listener instanceof ServletRequestAttributeListener attributeListener) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Adding ServletRequestAttributeListener: {0}", listener); } attributeListeners.add(attributeListener); } if (listener instanceof ServletRequestListener requestListener) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Adding ServletRequestlistener: {0}", listener); } requestListeners.add(requestListener); } } @Override public void attributeAdded(HttpServletRequest request, String name, Object value) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Attribute: {0} added with value: {1}", name, value); } attributeListeners.stream().forEach(listener -> listener.attributeAdded(new ServletRequestAttributeEvent( request.getServletContext(), request, name, value))); } @Override public void attributeRemoved(HttpServletRequest request, String name, Object value) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Attribute: {0} removed with value: {1}", name, value); } attributeListeners.stream().forEach(listener -> listener.attributeRemoved(new ServletRequestAttributeEvent( request.getServletContext(), request, name, value))); } @Override public void attributeReplaced(HttpServletRequest request, String name, Object value) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Attribute: {0} replaced with value: {1}", name, value); } attributeListeners.stream().forEach(listener -> listener.attributeReplaced(new ServletRequestAttributeEvent( request.getServletContext(), request, name, value))); } @Override public void requestDestroyed(ServletRequest request) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Request: {0} destroyed", request); } requestListeners.stream().forEach(servletRequestListener -> servletRequestListener.requestDestroyed(new ServletRequestEvent( request.getServletContext(), request))); } @Override public void requestInitialized(ServletRequest request) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Request: {0} initialized", request); } requestListeners.stream().forEach(servletRequestListener -> servletRequestListener.requestInitialized(new ServletRequestEvent( request.getServletContext(), request))); } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultTaglibDescriptor.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import jakarta.servlet.descriptor.TaglibDescriptor; /** * The default TaglibDescriptor. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultTaglibDescriptor implements TaglibDescriptor { /** * Stores the taglib location. */ private String taglibLocation; /** * Stores the taglib URI. */ private String taglibURI; /** * Constructor. */ public DefaultTaglibDescriptor() { } @Override public String getTaglibLocation() { return taglibLocation; } @Override public String getTaglibURI() { return taglibURI; } /** * Set the taglib location. * * @param taglibLocation the taglib location. */ public void setTaglibLocation(String taglibLocation) { this.taglibLocation = taglibLocation; } /** * Set the taglib URI. * * @param taglibURI the taglib URI. */ public void setTaglibURI(String taglibURI) { this.taglibURI = taglibURI; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplication.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.resource.api.Resource; import cloud.piranha.core.api.AnnotationInfo; import cloud.piranha.core.api.ServletEnvironment; import static cloud.piranha.core.api.ServletEnvironment.UNAVAILABLE; import cloud.piranha.core.api.WebApplication; import static cloud.piranha.core.api.WebApplication.Status.DECLARED; import static cloud.piranha.core.api.WebApplication.Status.ERROR; import static cloud.piranha.core.api.WebApplication.Status.INITIALIZED; import static cloud.piranha.core.api.WebApplication.Status.SERVICING; import static cloud.piranha.core.api.WebApplication.Status.SETUP; import cloud.piranha.core.api.WebApplicationRequestMapper; import jakarta.servlet.DispatcherType; import jakarta.servlet.Filter; import jakarta.servlet.FilterRegistration; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.Servlet; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletContextAttributeEvent; import jakarta.servlet.ServletContextAttributeListener; import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletContextListener; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRegistration; import jakarta.servlet.ServletRegistration.Dynamic; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletRequestAttributeListener; import jakarta.servlet.ServletRequestListener; import jakarta.servlet.ServletResponse; import jakarta.servlet.SessionCookieConfig; import jakarta.servlet.SessionTrackingMode; import jakarta.servlet.UnavailableException; import jakarta.servlet.annotation.HandlesTypes; import jakarta.servlet.descriptor.JspConfigDescriptor; import jakarta.servlet.http.HttpSessionAttributeListener; import jakarta.servlet.http.HttpSessionIdListener; import jakarta.servlet.http.HttpSessionListener; import jakarta.servlet.http.WebConnection; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.System.Logger; import static java.lang.System.Logger.Level.DEBUG; import static java.lang.System.Logger.Level.WARNING; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import static java.util.Collections.enumeration; import static java.util.Collections.reverse; import static java.util.Collections.unmodifiableMap; import java.util.Enumeration; import java.util.EventListener; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import static java.util.Objects.requireNonNull; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import static java.util.function.Predicate.isEqual; import static java.util.function.Predicate.not; import java.util.stream.Collectors; import static java.util.stream.Collectors.toSet; import java.util.stream.Stream; import cloud.piranha.core.api.WebApplicationManager; import static java.lang.System.Logger.Level.INFO; import static java.lang.System.Logger.Level.TRACE; import java.util.Arrays; /** * The default WebApplication. * *

* The filters field is backed by a LinkedHashMap so we get an * insertion-order key set. If you change this, be aware that methods using this * field should be changed to account for that. *

* *

* The servlets field is backed by a LinkedHashMap so we get an * insertion-order key set. If you change this, be aware that methods using this * field should be changed to account for that. *

* * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultWebApplication implements WebApplication { /** * Stores the 'Illegal to add listener because state is not SETUP' message. */ private static final String ILLEGAL_TO_ADD_LISTENER = "Illegal to add listener because state is not SETUP"; /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(DefaultWebApplication.class.getName()); /** * Stores the attributes. */ protected final Map attributes; /** * Stores the class loader. */ protected ClassLoader classLoader; /** * Stores the context path. */ protected String contextPath; /** * Stores the default servlet (if any). */ protected Servlet defaultServlet; /** * Stores the boolean flag indicating if the web application is * distributable. */ protected boolean distributable; /** * Stores the effective major version. */ protected int effectiveMajorVersion = -1; /** * Stores the effective minor version. */ protected int effectiveMinorVersion = -1; /** * Stores the servlet context name. */ protected String servletContextName; /** * Stores the status. */ protected Status status; /** * Stores the virtual server name. */ protected String virtualServerName = "server"; /** * Stores the response character encoding. */ protected String responseCharacterEncoding; /** * Stores the active requests and the associated responses. */ protected final Map requests; /** * Stores the active responses and the associated requests. */ protected final Map responses; /** * Stores the servlet container initializers. */ protected final List initializers; /** * Stores the init parameters. */ protected final Map initParameters; /** * Stores the servlet environments */ protected final Map servletEnvironments; /** * Stores the filters. */ protected final Map filters; // ### Listeners /** * Stores the servlet context attribute listeners. */ protected final List contextAttributeListeners; /** * Stores the servlet context listeners that were declared in web.xml, * web-fragment.xml, or via annotations */ protected final List declaredContextListeners; /** * Stores the servlet context listeners that were not declared in web.xml, * web-fragment.xml, or via annotations */ protected final List contextListeners; /** * Stores the invocation finder, which finds a Servlet, Filter(chain) and * variants thereof to invoke for a given request path. */ protected DefaultInvocationFinder invocationFinder; /** * Stores the metadata complete flag. */ protected boolean metadataComplete; /** * Stores the request character encoding. */ protected String requestCharacterEncoding; /** * The source object where this web application instance originates from, * i.e. the artifact this was last passed into by the container. Compare to * the source object of an event. */ protected Object source; /** * When we're in tainted mode, we have to throw exceptions for a large * number of methods. * * Tainted mode is required for ServletContextListeners which have not been * declared. At the moment of writing it's not clear why this tainted mode * is needed. */ protected boolean tainted; /** * Stores the web application request mapper. */ protected WebApplicationRequestMapper webApplicationRequestMapper; /** * Stores the web application manager. */ protected WebApplicationManager manager; /** * Stores the mime types. */ private final Map mimeTypes = new HashMap<>(); /** * Constructor. */ public DefaultWebApplication() { attributes = new HashMap<>(1); classLoader = getClass().getClassLoader(); contextAttributeListeners = new ArrayList<>(1); declaredContextListeners = new ArrayList<>(1); contextListeners = new ArrayList<>(1); contextPath = ""; filters = new LinkedHashMap<>(1); initParameters = new ConcurrentHashMap<>(1); initializers = new ArrayList<>(1); requests = new ConcurrentHashMap<>(1); responses = new ConcurrentHashMap<>(1); invocationFinder = new DefaultInvocationFinder(this); servletContextName = UUID.randomUUID().toString(); servletEnvironments = new LinkedHashMap<>(); webApplicationRequestMapper = new DefaultWebApplicationRequestMapper(); manager = new DefaultWebApplicationManager(); manager.getHttpSessionManager().setWebApplication(this); manager.getDispatcherManager().setWebApplication(this); mimeTypes.put("css", "text/css"); mimeTypes.put("js", "text/javascript"); mimeTypes.put("ico", "image/x-icon"); mimeTypes.put("svg", "image/svg+xml"); mimeTypes.put("png", "image/png"); mimeTypes.put("ttf", "font/ttf"); mimeTypes.put("html", "text/html"); mimeTypes.put("htm", "text/html"); mimeTypes.put("text", "text/plain"); mimeTypes.put("txt", "text/plain"); status = SETUP; } @Override public FilterRegistration.Dynamic addFilter(String filterName, Class filterClass) { return addFilter(filterName, filterClass.getName()); } @Override public FilterRegistration.Dynamic addFilter(String filterName, String className) { checkTainted(); checkServicing(); if (filterName == null || filterName.trim().equals("")) { throw new IllegalArgumentException("Filter name cannot be null or empty"); } DefaultFilterEnvironment defaultFilterEnvironment; if (filters.containsKey(filterName)) { defaultFilterEnvironment = filters.get(filterName); if (defaultFilterEnvironment.getClassName() != null) { // Filter already set, can't override return null; } } else { defaultFilterEnvironment = new DefaultFilterEnvironment(); defaultFilterEnvironment.setFilterName(filterName); defaultFilterEnvironment.setWebApplication(this); filters.put(filterName, defaultFilterEnvironment); } defaultFilterEnvironment.setClassName(className); return defaultFilterEnvironment; } @Override public FilterRegistration.Dynamic addFilter(String filterName, Filter filter) { checkTainted(); checkServicing(); if (filters.containsKey(filterName)) { DefaultFilterEnvironment filterEnvironment = filters.get(filterName); if (filterEnvironment.getClassName() != null) { // Filter already set, can't override return null; } } DefaultFilterEnvironment filterEnvironment = new DefaultFilterEnvironment(this, filterName, filter); filters.put(filterName, filterEnvironment); return filterEnvironment; } @Override public Set addFilterMapping(Set dispatcherTypes, String filterName, boolean isMatchAfter, String... urlPatterns) { if (isMatchAfter) { return webApplicationRequestMapper.addFilterMapping(dispatcherTypes, filterName, urlPatterns); } return webApplicationRequestMapper.addFilterMappingBeforeExisting(dispatcherTypes, filterName, urlPatterns); } @Override public void addInitializer(String className) { LOGGER.log(DEBUG, "Adding ServletContainerInitializer: " + className); try { @SuppressWarnings("unchecked") Class clazz = (Class) getClassLoader().loadClass(className); initializers.add(clazz.getDeclaredConstructor().newInstance()); } catch (Exception exception) { LOGGER.log(WARNING, () -> "Unable to add initializer: " + className, exception); } } @Override public void addInitializer(ServletContainerInitializer servletContainerInitializer) { initializers.add(servletContainerInitializer); } @Override public ServletRegistration.Dynamic addJspFile(String servletName, String jspFile) { if (status != SETUP && status != DECLARED) { throw new IllegalStateException("Illegal to add JSP file because state is not SETUP"); } if (isEmpty(servletName)) { throw new IllegalArgumentException("Servlet name cannot be null or empty"); } return manager.getJspManager().addJspFile(this, servletName, jspFile); } @SuppressWarnings("unchecked") @Override public void addListener(String className) { checkTainted(); if (status != SETUP && status != DECLARED) { throw new IllegalStateException(ILLEGAL_TO_ADD_LISTENER); } try { addListener((Class) getClassLoader().loadClass(className)); } catch (ClassNotFoundException exception) { LOGGER.log(WARNING, () -> "Unable to add listener: " + className, exception); } } @Override public void addListener(Class type) { checkTainted(); if (status != SETUP && status != DECLARED) { throw new IllegalStateException(ILLEGAL_TO_ADD_LISTENER); } try { addListener(createListener(type)); } catch (ServletException exception) { LOGGER.log(WARNING, () -> "Unable to add listener: " + type, exception); } } @Override public void addListener(T listener) { checkTainted(); if (status != SETUP && status != DECLARED) { throw new IllegalStateException(ILLEGAL_TO_ADD_LISTENER); } if (listener instanceof ServletContextListener servletContextListener) { if (source != null && !(source instanceof ServletContainerInitializer)) { throw new IllegalArgumentException("Illegal to add ServletContextListener because this context was not passed to a ServletContainerInitializer"); } if (status == DECLARED) { contextListeners.add(servletContextListener); } else { declaredContextListeners.add(servletContextListener); } } if (listener instanceof ServletContextAttributeListener servletContextAttributeListener) { contextAttributeListeners.add(servletContextAttributeListener); } if (listener instanceof ServletRequestListener servletRequestListener) { getManager().getServletRequestManager().addListener(servletRequestListener); } if (listener instanceof ServletRequestAttributeListener servletRequestAttributeListener) { getManager().getServletRequestManager().addListener(servletRequestAttributeListener); } if (listener instanceof HttpSessionAttributeListener) { getManager().getHttpSessionManager().addListener(listener); } if (listener instanceof HttpSessionIdListener) { getManager().getHttpSessionManager().addListener(listener); } if (listener instanceof HttpSessionListener) { getManager().getHttpSessionManager().addListener(listener); } } @Override public void addMimeType(String extension, String mimeType) { mimeTypes.put(extension.toLowerCase(), mimeType); } @Override public void addResource(Resource resource) { getManager().getResourceManager().addResource(resource); } @Override public Dynamic addServlet(String servletName, Class servletClass) { return addServlet(servletName, servletClass.getName()); } @Override public Dynamic addServlet(String servletName, String className) { checkTainted(); checkServicing(); DefaultServletEnvironment servletEnvironment = servletEnvironments.get(servletName); if (servletEnvironment == null) { servletEnvironment = new DefaultServletEnvironment(this, servletName); if (className != null && !className.isBlank()) { servletEnvironment.setClassName(className); } servletEnvironments.put(servletName, servletEnvironment); } else { if (!isEmpty(servletEnvironment.getClassName())) { // Servlet already set, can't override return null; } if (className != null && !className.isBlank()) { servletEnvironment.setClassName(className); } } return servletEnvironment; } @Override public Dynamic addServlet(String servletName, Servlet servlet) { checkTainted(); checkServicing(); if (servletEnvironments.containsKey(servletName)) { DefaultServletEnvironment servletEnvironment = servletEnvironments.get(servletName); if (!isEmpty(servletEnvironment.getClassName())) { // Servlet already set, can't override return null; } } DefaultServletEnvironment servletEnvironment = new DefaultServletEnvironment(this, servletName, servlet); servletEnvironments.put(servletName, servletEnvironment); return servletEnvironment; } @Override public Set addServletMapping(String servletName, String... urlPatterns) { return webApplicationRequestMapper.addServletMapping(servletName, urlPatterns); } /** * Attribute added. * * @param name the name. * @param value the value. */ private void attributeAdded(String name, Object value) { contextAttributeListeners.stream().forEach(listener -> listener.attributeAdded(new ServletContextAttributeEvent(this, name, value))); } /** * Attributed removed. * * @param name the name. * @param previousValue the previous value. */ private void attributeRemoved(String name, Object previousValue) { contextAttributeListeners.stream().forEach(listener -> listener.attributeRemoved(new ServletContextAttributeEvent(this, name, previousValue))); } /** * Attribute removed. * * @param name the name. * @param value the value. */ private void attributeReplaced(String name, Object value) { contextAttributeListeners.stream().forEach(listener -> listener.attributeReplaced(new ServletContextAttributeEvent(this, name, value))); } /** * Make sure the application is not servicing when this method is called. */ private void checkServicing() { if (status == SERVICING) { throw new IllegalStateException("Cannot call this after web application has initialized"); } } /** * Make sure the application is not tainted when this method is called. */ private void checkTainted() { if (tainted) { throw new UnsupportedOperationException( "ServletContext is in tainted mode (as required by spec)."); } } @Override public T createFilter(Class filterClass) throws ServletException { checkTainted(); return manager.getObjectInstanceManager().createFilter(filterClass); } @Override public T createListener(Class clazz) throws ServletException { checkTainted(); T result = manager.getObjectInstanceManager().createListener(clazz); boolean ok = false; if (result instanceof ServletContextListener || result instanceof ServletContextAttributeListener || result instanceof ServletRequestListener || result instanceof ServletRequestAttributeListener || result instanceof HttpSessionAttributeListener || result instanceof HttpSessionIdListener || result instanceof HttpSessionListener) { ok = true; } if (!ok) { LOGGER.log(WARNING, "Unable to create listener: {0}", clazz); throw new IllegalArgumentException("Invalid type"); } return result; } @Override public T createServlet(Class servletClass) throws ServletException { checkTainted(); return manager.getObjectInstanceManager().createServlet(servletClass); } @Override public void declareRoles(String... roles) { manager.getSecurityManager().declareRoles(roles); } @Override public WebApplication destroy() { verifyState(INITIALIZED, "Unable to destroy web application"); servletEnvironments.values().stream().forEach(servletEnv -> servletEnv.getServlet().destroy()); servletEnvironments.clear(); reverse(contextListeners); contextListeners.stream().forEach(listener -> listener.contextDestroyed(new ServletContextEvent(this))); contextListeners.clear(); reverse(declaredContextListeners); declaredContextListeners.stream().forEach(listener -> listener.contextDestroyed(new ServletContextEvent(this))); declaredContextListeners.clear(); status = SETUP; return this; } @Override public Object getAttribute(String name) { Objects.requireNonNull(name); return attributes.get(name); } @Override public Enumeration getAttributeNames() { return enumeration(attributes.keySet()); } @Override public ClassLoader getClassLoader() { return classLoader; } @Override public ServletContext getContext(String uripath) { return null; } @Override public String getContextPath() { return contextPath; } @Override public Servlet getDefaultServlet() { return defaultServlet; } @Override public Set getDefaultSessionTrackingModes() { return getManager().getHttpSessionManager().getDefaultSessionTrackingModes(); } @Override public int getEffectiveMajorVersion() { if (effectiveMajorVersion == -1) { return getMajorVersion(); } return effectiveMajorVersion; } @Override public int getEffectiveMinorVersion() { if (effectiveMinorVersion == -1) { return getMinorVersion(); } return effectiveMinorVersion; } @Override public Set getEffectiveSessionTrackingModes() { return getManager().getHttpSessionManager().getEffectiveSessionTrackingModes(); } /** * Returns the file path or the first nested folder * * @apiNote *

* Examples. *

{@code
     *  getFileOrFirstFolder("/rootFolder", "/rootFolder/file.html").equals("/rootFolder/file.html")
     * }
* *
{@code
     *  getFileOrFirstFolder("/rootFolder", "/rootFolder/nestedFolder/file.html").equals("/rootFolder/nestedFolder/")
     * }
* *
{@code
     *  getFileOrFirstFolder("/rootFolder/nestedFolder", "/rootFolder/nestedFolder/file.html")
     *      .equals("/rootFolder/nestedFolder/file.html")
     * }
* * @param path the path of root folder * @param resource the resource that is a file directory or file * @return the file path or the first nested folder */ private String getFileOrFirstFolder(String path, String resource) { String normalizedPath = path.endsWith("/") ? path : path + "/"; String[] split = resource.replace(normalizedPath, "/").split("/"); // It's a directory if (split.length > 2) { return normalizedPath + split[1] + "/"; } // It's a file return normalizedPath + split[1]; } @Override public FilterRegistration getFilterRegistration(String filterName) { checkTainted(); return filters.get(filterName); } @Override public Map getFilterRegistrations() { checkTainted(); return unmodifiableMap(filters); } @Override public String getInitParameter(String name) { return initParameters.get(name); } @Override public Enumeration getInitParameterNames() { return enumeration(initParameters.keySet()); } @Override public List getInitializers() { return initializers; } /** * Get the invocation request dispatcher. * * @param servletInvocation the servlet invocation. * @return the invocation request dispatcher. */ private DefaultServletRequestDispatcher getInvocationDispatcher(DefaultServletInvocation servletInvocation) { return new DefaultServletRequestDispatcher(servletInvocation, this); } @Override public JspConfigDescriptor getJspConfigDescriptor() { checkTainted(); return manager.getJspManager().getJspConfigDescriptor(); } @Override public int getMajorVersion() { return 6; } @Override public WebApplicationManager getManager() { return manager; } @Override public Collection getMappings(String servletName) { return webApplicationRequestMapper.getServletMappings(servletName); } @Override public String getMimeType(String filename) { String mimeType = null; if (filename != null && filename.contains(".")) { mimeType = mimeTypes.get(filename.substring(filename.lastIndexOf(".") + 1).toLowerCase()); } return mimeType; } @Override public int getMinorVersion() { return 0; } @Override public RequestDispatcher getNamedDispatcher(String name) { return getManager().getDispatcherManager().getNamedDispatcher(name); } @Override public String getRealPath(String path) { String realPath = null; try { URL resourceUrl = getResource(path); if (resourceUrl != null && "file".equals(resourceUrl.getProtocol())) { File file = new File(resourceUrl.toURI()); if (file.exists()) { realPath = file.toString(); } } } catch (MalformedURLException | URISyntaxException | IllegalArgumentException exception) { LOGGER.log(WARNING, () -> "Unable to get real path: " + path, exception); } return realPath; } @Override public ServletRequest getRequest(ServletResponse response) { return responses.get(response); } @Override public String getRequestCharacterEncoding() { return requestCharacterEncoding; } @Override public DefaultServletRequestDispatcher getRequestDispatcher(String path) { try { DefaultServletInvocation servletInvocation = invocationFinder.findServletInvocationByPath(null, path, null); if (servletInvocation == null) { return null; } return getInvocationDispatcher(servletInvocation); } catch (IOException | ServletException e) { LOGGER.log(WARNING, "Error occurred while getting request dispatcher", e); return null; } } @Override public URL getResource(String location) throws MalformedURLException { if (!location.startsWith("/")) { throw new MalformedURLException("Location " + location + " must start with a /"); } return getManager().getResourceManager().getResource(location); } @Override public InputStream getResourceAsStream(String location) { return getManager().getResourceManager().getResourceAsStream(location); } @Override public Set getResourcePaths(String path) { if (path == null) { return null; } if (!path.startsWith("/")) { throw new IllegalArgumentException("Path must start with /"); } return getResourcePathsImpl(path); } /** * Returns a directory-like listing of all the paths to resources within the * web application whose longest sub-path matches the supplied path * argument. * * @param path the partial path used to match the resources * @return a Set containing the directory listing, or null if there are no * resources in the web application whose path begins with the supplied * path. */ private Set getResourcePathsImpl(String path) { Set collect = getManager().getResourceManager().getAllLocations() .filter(resource -> resource.startsWith(path)) .filter(not(isEqual(path))) .map(resource -> getFileOrFirstFolder(path, resource)) .collect(toSet()); if (collect.isEmpty()) { return null; } return collect; } @Override public ServletResponse getResponse(ServletRequest request) { return requests.get(request); } @Override public String getResponseCharacterEncoding() { return responseCharacterEncoding; } @Override public String getServerInfo() { return "DefaultWebApplication/6.0"; } @Override public String getServletContextName() { return servletContextName; } @Override public ServletRegistration getServletRegistration(String servletName) { checkTainted(); return servletEnvironments.get(servletName); } @Override public Map getServletRegistrations() { checkTainted(); return unmodifiableMap(servletEnvironments); } @Override public SessionCookieConfig getSessionCookieConfig() { checkTainted(); return getManager().getHttpSessionManager().getSessionCookieConfig(); } @Override public int getSessionTimeout() { return getManager().getHttpSessionManager().getSessionTimeout(); } @Override public Status getStatus() { return status; } @Override public String getVirtualServerName() { return virtualServerName; } @Override public WebApplication initialize() { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Initializing web application at {0}", contextPath); } verifyState(SETUP, "Unable to initialize web application"); ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(classLoader); initializeInitializers(); initializeFilters(); initializeServlets(); initializeFinish(); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } return this; } /** * Initialize the filters. */ protected void initializeFilters() { if (status == SETUP || status == DECLARED) { List filterNames = new ArrayList<>(filters.keySet()); filterNames.stream().map(filters::get).forEach(environment -> { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Initializing filter: {0}", environment.getFilterName()); } try { environment.initialize(); environment.getFilter().init(environment); if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Initialized filter: {0}", environment.getFilterName()); } } catch (Throwable e) { LOGGER.log(WARNING, () -> "Unable to initialize filter: " + environment.getFilterName(), e); environment.setStatus(UNAVAILABLE); } }); } } /** * Finish the initialization. */ protected void initializeFinish() { if (status == SETUP || status == DECLARED) { status = INITIALIZED; if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Initialized web application at {0}", contextPath); } } if (status == ERROR) { LOGGER.log(WARNING, () -> "An error occurred initializing webapplication at " + contextPath); } } /** * Initialize the servlet container initializers. */ protected void initializeInitializers() { boolean error = false; for (ServletContainerInitializer initializer : initializers) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Initializing initializer: {0}", initializer.getClass().getName()); } try { HandlesTypes annotation = initializer.getClass().getAnnotation(HandlesTypes.class); Set> classes = Collections.emptySet(); if (annotation != null) { Class[] value = annotation.value(); if (manager.getAnnotationManager() != null) { // Get instances Stream> instances = manager.getAnnotationManager() .getInstances(value).stream(); // Get classes by target type List> annotations = manager.getAnnotationManager() .getAnnotations(value); Stream> classStream = annotations.stream().map(AnnotationInfo::getTargetType); classes = Stream.concat(instances, classStream).collect(Collectors.toSet()); classes.addAll(manager.getAnnotationManager().getAnnotatedClasses(annotation.value())); } /* * If we have a HandlesTypes manager we use it to get the * set of classes we need to pass to the onStartup method. */ if (manager.getHandlesTypesManager() != null && value != null) { Set> handlesTypesClasses = manager.getHandlesTypesManager().getClasses( Arrays.stream(value) .collect(Collectors.toSet())); if (handlesTypesClasses != null && !handlesTypesClasses.isEmpty()) { if (classes == null) { classes = handlesTypesClasses; } else { classes.addAll(handlesTypesClasses); } } } } try { source = initializer; initializer.onStartup(classes, this); if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Initialized initializer: {0}", initializer.getClass().getName()); } } finally { source = null; } } catch (Throwable e) { LOGGER.log(WARNING, () -> "Initializer " + initializer.getClass().getName() + " failing onStartup", e); error = true; } } if (!error) { List listeners = new ArrayList<>(declaredContextListeners); listeners.stream().forEach(listener -> { try { source = listener; listener.contextInitialized(new ServletContextEvent(this)); } finally { source = null; } }); try { tainted = true; listeners = new ArrayList<>(contextListeners); listeners.stream().forEach(listener -> { source = listener; listener.contextInitialized(new ServletContextEvent(this)); }); } finally { tainted = false; source = null; } } else { status = ERROR; } } /** * Initialize the Servlet. * * @param environment the Servlet environment. */ protected void initializeServlet(DefaultServletEnvironment environment) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Initializing servlet: {0}", environment.servletName); } try { if (environment.getServlet() == null) { Class clazz = environment.getServletClass(); if (clazz == null) { ClassLoader loader = getClassLoader(); if (loader == null) { loader = getClass().getClassLoader(); } if (loader == null) { loader = ClassLoader.getSystemClassLoader(); } clazz = (Class) loader.loadClass(environment.getClassName()); } environment.setServlet(createServlet(clazz)); } environment.getServlet().init(environment); if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Initialized servlet: {0}", environment.servletName); } } catch (Throwable e) { LOGGER.log(WARNING, () -> "Unable to initialize servlet: " + environment.className, e); environment.setStatus(ServletEnvironment.UNAVAILABLE); environment.setUnavailableException(e); // Servlet:SPEC:11 - If a permanent unavailability is indicated by the UnavailableException, the servlet container must ... call its destroy method if (isPermanentlyUnavailable(environment) && environment.getServlet() != null) { try { environment.getServlet().destroy(); } catch (Exception e2) { e.addSuppressed(e2); } } environment.setServlet(null); } } /** * Initialize the servlets. */ protected void initializeServlets() { if (status == SETUP || status == DECLARED) { List servletsToBeRemoved = new ArrayList<>(); List servletNames = new ArrayList<>(servletEnvironments.keySet()); servletNames.stream().map(servletEnvironments::get).forEach(environment -> { initializeServlet(environment); if (isPermanentlyUnavailable(environment)) { servletsToBeRemoved.add(environment.getServletName()); } }); for (String servletName : servletsToBeRemoved) { // Servlet:SPEC:11 - If a permanent unavailability is indicated by the UnavailableException, the servlet container must // remove the servlet from service ... and release the servlet instance. servletEnvironments.remove(servletName); } } } @Override public boolean isDistributable() { return distributable; } /** * Is the string null or empty. * * @param string the string * @return true if it is, false otherwise. */ private boolean isEmpty(String string) { return string == null || string.isEmpty(); } @Override public boolean isInitialized() { return status.ordinal() >= INITIALIZED.ordinal() && status.ordinal() < ERROR.ordinal(); } @Override public boolean isMetadataComplete() { return metadataComplete; } /** * Is the servlet permanently unavailable. * * @param environment the Servlet environment. * @return true if it is, false otherwise. */ private boolean isPermanentlyUnavailable(DefaultServletEnvironment environment) { boolean permanent = false; if (environment.getUnavailableException() instanceof UnavailableException ue) { permanent = ue.isPermanent(); } return permanent; } @Override public boolean isServicing() { return status == SERVICING; } @Override public void linkRequestAndResponse(ServletRequest request, ServletResponse response) { requests.put(request, response); responses.put(response, request); } @Override public void log(String message, Throwable throwable) { LOGGER.log(INFO, message, throwable); } @Override public void log(String message) { LOGGER.log(INFO, message); } @Override public void removeAttribute(String name) { attributeRemoved(name, attributes.remove(name)); } @Override public String removeServletMapping(String urlPattern) { return webApplicationRequestMapper.removeServletMapping(urlPattern); } @Override public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Servicing request: {0} and response: {1}", request, response); } verifyState(SERVICING, "Unable to service request"); verifyRequestResponseTypes(request, response); linkRequestAndResponse(request, response); getManager().getServletRequestManager().requestInitialized(request); DefaultWebApplicationRequest webAppRequest = (DefaultWebApplicationRequest) request; DefaultWebApplicationResponse webAppResponse = (DefaultWebApplicationResponse) response; // Obtain a reference to the target servlet invocation, which includes the Servlet itself and/or Filters, as well as mapping data DefaultServletInvocation servletInvocation = invocationFinder.findServletInvocationByPath(webAppRequest.getServletPath(), webAppRequest.getPathInfo()); // Dispatch using the REQUEST dispatch type. This will invoke the Servlet and/or Filters if present and available. getInvocationDispatcher(servletInvocation).request(webAppRequest, webAppResponse); if (!response.isCommitted() && webAppRequest.getSession(false) instanceof DefaultHttpSession session) { session.setLastAccessedTime(System.currentTimeMillis()); } getManager().getServletRequestManager().requestDestroyed(request); if (!webAppRequest.isAsyncStarted() && !webAppRequest.isUpgraded()) { unlinkRequestAndResponse(request, response); } if (webAppRequest.isUpgraded()) { WebConnection connection = new DefaultWebConnection(webAppRequest, webAppResponse); webAppRequest.getUpgradeHandler().init(connection); } if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Serviced request: {0} and response: {1}", request, response); } } @Override public void setAttribute(String name, Object value) { Objects.requireNonNull(name); if (value != null) { boolean added = true; if (attributes.containsKey(name)) { added = false; } Object previousValue = attributes.put(name, value); if (added) { attributeAdded(name, value); } else { attributeReplaced(name, previousValue); } } else { removeAttribute(name); } } @Override public void setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } @Override public void setContextPath(String contextPath) { LOGGER.log(DEBUG, "Setting context path to: {0}", contextPath); this.contextPath = contextPath; } @Override public void setDefaultServlet(Servlet defaultServlet) { this.defaultServlet = defaultServlet; } @Override public void setDistributable(boolean distributable) { this.distributable = distributable; } @Override public void setEffectiveMajorVersion(int effectiveMajorVersion) { this.effectiveMajorVersion = effectiveMajorVersion; } @Override public void setEffectiveMinorVersion(int effectiveMinorVersion) { this.effectiveMinorVersion = effectiveMinorVersion; } @Override public boolean setInitParameter(String name, String value) { requireNonNull(name); checkTainted(); if (status != SETUP && status != DECLARED) { throw new IllegalStateException("Cannot set init parameter once web application is initialized"); } boolean result = true; if (initParameters.containsKey(name)) { result = false; } else { initParameters.put(name, value); } return result; } @Override public void setJspConfigDescriptor(JspConfigDescriptor jspConfigDescriptor) { getManager().getJspManager().setJspConfigDescriptor(jspConfigDescriptor); } @Override public void setMetadataComplete(boolean metadataComplete) { this.metadataComplete = metadataComplete; } @Override public void setRequestCharacterEncoding(String requestCharacterEncoding) { this.requestCharacterEncoding = requestCharacterEncoding; } @Override public void setResponseCharacterEncoding(String responseCharacterEncoding) { this.responseCharacterEncoding = responseCharacterEncoding; } @Override public void setSessionTimeout(int sessionTimeout) { checkTainted(); if (status != SETUP && status != DECLARED) { throw new IllegalStateException("Illegal to set session timeout because state is not SETUP"); } getManager().getHttpSessionManager().setSessionTimeout(sessionTimeout); } @Override public void setServletContextName(String servletContextName) { this.servletContextName = servletContextName; } @Override public void setSessionTrackingModes(Set sessionTrackingModes) { checkTainted(); checkServicing(); getManager().getHttpSessionManager().setSessionTrackingModes(sessionTrackingModes); } @Override public void setStatus(Status status) { this.status = status; } @Override public void setVirtualServerName(String virtualServerName) { this.virtualServerName = virtualServerName; } @Override public void setWebApplicationRequestMapper(WebApplicationRequestMapper webApplicationRequestMapper) { this.webApplicationRequestMapper = webApplicationRequestMapper; } @Override public WebApplication start() { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Starting web application at {0}", contextPath); } verifyState(INITIALIZED, "Unable to start servicing"); status = SERVICING; if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Started web application at {0}", contextPath); } return this; } @Override public WebApplication stop() { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Stopping web application at {0}", contextPath); } verifyState(SERVICING, "Unable to stop servicing"); status = INITIALIZED; if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Stopped web application at {0}", contextPath); } return this; } @Override public void unlinkRequestAndResponse(ServletRequest request, ServletResponse response) { requests.remove(request); responses.remove(response); } /** * Verify the request/response types. * * @param request the request. * @param response the response. * @throws ServletException when request or response is invalid. */ protected void verifyRequestResponseTypes(ServletRequest request, ServletResponse response) throws ServletException { if (!(request instanceof DefaultWebApplicationRequest) || !(response instanceof DefaultWebApplicationResponse)) { throw new ServletException("Invalid request or response"); } } /** * Verify the web application state. * * @param desiredStatus the desired status. * @param message the message. */ protected void verifyState(Status desiredStatus, String message) { if (status != desiredStatus) { throw new RuntimeException(message); } } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplicationBuilder.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.resource.api.Resource; import cloud.piranha.resource.impl.DirectoryResource; import jakarta.servlet.Filter; import jakarta.servlet.FilterRegistration; import jakarta.servlet.Servlet; import jakarta.servlet.ServletRegistration; import jakarta.servlet.SessionTrackingMode; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * The DefaultWebApplication builder. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultWebApplicationBuilder { /** * Stores the filter init parameters map. */ private final Map> filterInitParameters = new HashMap<>(); /** * Stores the filters. */ private final Map filters = new HashMap<>(); /** * Stores the init parameters. */ private final Map initParams = new HashMap<>(); /** * Stores the list of resources. */ private final List resources = new ArrayList<>(); /** * Stores the servlet init parameters map. */ private final Map> servletInitParameters = new HashMap<>(); /** * Stores the servlet mappings. */ private final Map servletMappings = new HashMap<>(); /** * Stores the servlets. */ private final Map servlets = new HashMap<>(); /** * Stores the session tracking modes. */ private Set sessionTrackingModes = null; /** * Constructor. */ public DefaultWebApplicationBuilder() { } /** * Build the web application. * * @return the web application. */ @SuppressWarnings("unchecked") public DefaultWebApplication build() { DefaultWebApplication webApplication = new DefaultWebApplication(); for (Resource resource : resources) { webApplication.addResource(resource); } for (Map.Entry filterEntry : filters.entrySet()) { String name = filterEntry.getKey(); Object value = filterEntry.getValue(); if (value instanceof String className) { webApplication.addFilter(name, className); } if (value instanceof Class clazz) { webApplication.addFilter(name, clazz); } if (value instanceof Filter filter) { webApplication.addFilter(name, filter); } if (filterInitParameters.containsKey(name)) { Map parameters = filterInitParameters.get(name); for (Map.Entry initParameter : parameters.entrySet()) { String initParameterName = initParameter.getKey(); String initParameterValue = initParameter.getValue(); FilterRegistration registration = webApplication.getFilterRegistration(name); registration.setInitParameter(initParameterName, initParameterValue); } } } for (Map.Entry entry : initParams.entrySet()) { String name = entry.getKey(); String value = entry.getValue(); webApplication.setInitParameter(name, value); } for (Map.Entry entry : servlets.entrySet()) { String name = entry.getKey(); Object value = entry.getValue(); if (value instanceof String className) { webApplication.addServlet(name, className); } if (value instanceof Class clazz) { webApplication.addServlet(name, clazz); } if (value instanceof Servlet servlet) { webApplication.addServlet(name, servlet); } if (servletInitParameters.containsKey(name)) { Map parameters = servletInitParameters.get(name); for (Map.Entry initParameter : parameters.entrySet()) { String initParameterName = initParameter.getKey(); String initParameterValue = initParameter.getValue(); ServletRegistration registration = webApplication.getServletRegistration(name); registration.setInitParameter(initParameterName, initParameterValue); } } } for (Map.Entry mappingEntry : servletMappings.entrySet()) { String servletName = mappingEntry.getKey(); String mapping = mappingEntry.getValue(); webApplication.addServletMapping(servletName, mapping); } if (sessionTrackingModes != null) { webApplication.setSessionTrackingModes(sessionTrackingModes); } return webApplication; } /** * Add a directory resource. * * @param directory the directory. * @return the web application builder. */ public DefaultWebApplicationBuilder directoryResource(String directory) { resources.add(new DirectoryResource(directory)); return this; } /** * Add a filter. * * @param name the name. * @param className the class name. * @return the web application builder. */ public DefaultWebApplicationBuilder filter(String name, String className) { filters.put(name, className); return this; } /** * Add a filter. * * @param name the name. * @param clazz the filter class. * @return the web application builder. */ public DefaultWebApplicationBuilder filter(String name, Class clazz) { filters.put(name, clazz); return this; } /** * Add a filter. * * @param name the name. * @param filter the filter. * @return the web application builder. */ public DefaultWebApplicationBuilder filter(String name, Filter filter) { filters.put(name, filter); return this; } /** * Set a filter init parameter. * * @param filterName the name of the filter. * @param name the name of the parameter. * @param value the value of the parameter. * @return the web application builder. */ public DefaultWebApplicationBuilder filterInitParam(String filterName, String name, String value) { Map initParameters = filterInitParameters.get(filterName); if (initParameters == null) { initParameters = new HashMap<>(); filterInitParameters.put(filterName, initParameters); } initParameters.put(name, value); return this; } /** * Add a init parameter. * * @param name the name. * @param value the value. * @return the web application builder. */ public DefaultWebApplicationBuilder initParam(String name, String value) { initParams.put(name, value); return this; } /** * Add a servlet. * * @param name the name of the servlet. * @param className the class name of the servlet. * @return the servlet builder. */ public DefaultWebApplicationBuilder servlet(String name, String className) { servlets.put(name, className); return this; } /** * Add a servlet. * * @param name the name of the servlet. * @param clazz the class of the servlet. * @return the web application builder. */ public DefaultWebApplicationBuilder servlet(String name, Class clazz) { servlets.put(name, clazz); return this; } /** * Add a servlet. * * @param name the name of the servlet. * @param servlet the Servlet. * @return the web application builder. */ public DefaultWebApplicationBuilder servlet(String name, Servlet servlet) { servlets.put(name, servlet); return this; } /** * Set a servlet init parameter. * * @param servletName the name of the servlet. * @param name the name of the parameter. * @param value the value of the parameter. * @return the web application builder. */ public DefaultWebApplicationBuilder servletInitParam(String servletName, String name, String value) { Map initParameters = servletInitParameters.get(servletName); if (initParameters == null) { initParameters = new HashMap<>(); servletInitParameters.put(servletName, initParameters); } initParameters.put(name, value); return this; } /** * Add a servlet mapping. * * @param servletName the servlet name. * @param mapping the mapping. * @return the web application builder. */ public DefaultWebApplicationBuilder servletMapping(String servletName, String mapping) { servletMappings.put(servletName, mapping); return this; } /** * Set the session tracking modes. * * @param sessionTrackingModes the session tracking modes. * @return the web application builder. */ public DefaultWebApplicationBuilder sessionTrackingModes(Set sessionTrackingModes) { this.sessionTrackingModes = sessionTrackingModes; return this; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplicationClassLoader.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplicationClassLoader; import cloud.piranha.resource.api.ResourceManager; import cloud.piranha.resource.impl.AliasedNamedResource; import cloud.piranha.resource.impl.DefaultResourceManager; import cloud.piranha.resource.impl.DefaultResourceManagerClassLoader; import cloud.piranha.resource.impl.DirectoryResource; import cloud.piranha.resource.impl.JarResource; import cloud.piranha.resource.impl.MultiReleaseResource; import java.io.File; /** * The default WebApplicationClassLoader. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultWebApplicationClassLoader extends DefaultResourceManagerClassLoader implements WebApplicationClassLoader { /** * Constructor. */ public DefaultWebApplicationClassLoader() { } /** * Constructor. * * @param baseDirectory the base directory. */ public DefaultWebApplicationClassLoader(File baseDirectory) { ResourceManager resourceManager = new DefaultResourceManager(); File classesDirectory = new File(baseDirectory, "WEB-INF/classes"); if (classesDirectory.exists()) { resourceManager.addResource(new MultiReleaseResource(new AliasedNamedResource(new DirectoryResource(classesDirectory), "cloud.piranha.modular.classes"))); } File libDirectory = new File(baseDirectory, "WEB-INF/lib"); if (libDirectory.exists()) { File[] jarFiles = libDirectory.listFiles(); if (jarFiles != null) { for (File jarFile : jarFiles) { resourceManager.addResource(new MultiReleaseResource(new JarResource(jarFile))); } } } setResourceManager(resourceManager); } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplicationExtensionContext.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import cloud.piranha.core.api.WebApplicationExtensionContext; import java.lang.System.Logger; import static java.lang.System.Logger.Level.WARNING; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; /** * The default web application extension context. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultWebApplicationExtensionContext implements WebApplicationExtensionContext { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(DefaultWebApplicationExtensionContext.class.getName()); /** * Stores the extensions. */ private final ArrayList extensions; /** * Stores the extension classes. */ private final ArrayList> extensionClasses; /** * Constructor. */ public DefaultWebApplicationExtensionContext() { extensions = new ArrayList<>(); extensionClasses = new ArrayList<>(); } /** * Add the extension. * * @param extension the extension. */ @Override public void add(Class extension) { try { WebApplicationExtension instance = extension.getDeclaredConstructor().newInstance(); instance.extend(this); extensions.add(instance); extensionClasses.add(extension); } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException ex) { LOGGER.log(WARNING, "Error occurred while adding extension", ex); } } /** * Add the extension. * * @param extension the extension. */ @Override public void add(WebApplicationExtension extension) { extension.extend(this); extensions.add(extension); extensionClasses.add(extension.getClass()); } /** * Configure the web application. * * @param webApplication the web application. */ public void configure(WebApplication webApplication) { for(WebApplicationExtension extension : extensions) { extension.configure(webApplication); } } /** * Remove the extension. * * @param extension the extension. */ @Override public void remove(Class extension) { if (extensionClasses.contains(extension)) { int index = extensionClasses.indexOf(extension); extensionClasses.remove(index); extensions.remove(index); } } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplicationInputStream.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplicationInputStream; import java.io.IOException; import java.lang.System.Logger; import static java.lang.System.Logger.Level.TRACE; /** * The default WebApplicationInputStream. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultWebApplicationInputStream extends WebApplicationInputStream { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(DefaultWebApplicationInputStream.class.getName()); /** * Constructor. */ public DefaultWebApplicationInputStream() { super(); } @Override public void close() throws IOException { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Closing input stream"); } super.close(); if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Closed input stream"); } } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplicationManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.AnnotationManager; import cloud.piranha.core.api.AsyncManager; import cloud.piranha.core.api.DispatcherManager; import cloud.piranha.core.api.ErrorPageManager; import cloud.piranha.core.api.HandlesTypesManager; import cloud.piranha.core.api.HttpSessionManager; import cloud.piranha.core.api.JspManager; import cloud.piranha.core.api.LocaleEncodingManager; import cloud.piranha.core.api.MultiPartManager; import cloud.piranha.core.api.ObjectInstanceManager; import cloud.piranha.core.api.SecurityManager; import cloud.piranha.core.api.WelcomeFileManager; import cloud.piranha.core.api.WebApplicationManager; import cloud.piranha.resource.api.ResourceManager; import cloud.piranha.resource.impl.DefaultResourceManager; import cloud.piranha.core.api.ServletRequestManager; /** * The default WebApplicationManager. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultWebApplicationManager implements WebApplicationManager { /** * Stores the annotation manager. */ protected AnnotationManager annotationManager; /** * Stores the async manager. */ protected AsyncManager asyncManager = new DefaultAsyncManager(); /** * Stores the dispatcher manager. */ protected DispatcherManager dispatcherManager = new DefaultDispatcherManager(); /** * Stores the error page manager. */ protected ErrorPageManager errorPageManager = new DefaultErrorPageManager(); /** * Stores the HandlesTypes manager. */ protected HandlesTypesManager handlesTypesManager; /** * Stores the HTTP session manager. */ protected HttpSessionManager httpSessionManager = new DefaultHttpSessionManager(); /** * Stores the JSP manager. */ protected JspManager jspManager = new DefaultJspManager(); /** * Stores the locale encoding manager. */ protected LocaleEncodingManager localeEncodingManager = new DefaultLocaleEncodingManager(); /** * Stores the multi-part manager. */ protected MultiPartManager multiPartManager = new DefaultMultiPartManager(); /** * Stores the object instance manager. */ protected ObjectInstanceManager objectInstanceManager = new DefaultObjectInstanceManager(); /** * Stores the resource manager. */ protected ResourceManager resourceManager = new DefaultResourceManager(); /** * Stores the security manager. */ protected SecurityManager securityManager = new DefaultSecurityManager(); /** * Stores the servlet request manager. */ protected ServletRequestManager servletRequestManager = new DefaultServletRequestManager(); /** * Stores the welcome file manager. */ protected WelcomeFileManager welcomeFileManager; /** * Constructor. */ public DefaultWebApplicationManager() { } @Override public AnnotationManager getAnnotationManager() { return annotationManager; } @Override public AsyncManager getAsyncManager() { return asyncManager; } @Override public DispatcherManager getDispatcherManager() { return dispatcherManager; } @Override public ErrorPageManager getErrorPageManager() { return errorPageManager; } @Override public HandlesTypesManager getHandlesTypesManager() { return handlesTypesManager; } @Override public HttpSessionManager getHttpSessionManager() { return httpSessionManager; } @Override public JspManager getJspManager() { return jspManager; } @Override public LocaleEncodingManager getLocaleEncodingManager() { return localeEncodingManager; } @Override public MultiPartManager getMultiPartManager() { return multiPartManager; } @Override public ObjectInstanceManager getObjectInstanceManager() { return objectInstanceManager; } @Override public ResourceManager getResourceManager() { return resourceManager; } @Override public SecurityManager getSecurityManager() { return securityManager; } @Override public ServletRequestManager getServletRequestManager() { return servletRequestManager; } @Override public WelcomeFileManager getWelcomeFileManager() { return welcomeFileManager; } @Override public void setAnnotationManager(AnnotationManager annotationManager) { this.annotationManager = annotationManager; } @Override public void setAsyncManager(AsyncManager asyncManager) { this.asyncManager = asyncManager; } @Override public void setErrorPageManager(ErrorPageManager errorPageManager) { this.errorPageManager = errorPageManager; } @Override public void setHandlesTypesManager(HandlesTypesManager handlesTypesManager) { this.handlesTypesManager = handlesTypesManager; } @Override public void setHttpSessionManager(HttpSessionManager httpSessionManager) { this.httpSessionManager = httpSessionManager; } @Override public void setJspManager(JspManager jspManager) { this.jspManager = jspManager; } @Override public void setLocaleEncodingManager(LocaleEncodingManager localeEncodingManager) { this.localeEncodingManager = localeEncodingManager; } @Override public void setMultiPartManager(MultiPartManager multiPartManager) { this.multiPartManager = multiPartManager; } @Override public void setObjectInstanceManager(ObjectInstanceManager objectInstanceManager) { this.objectInstanceManager = objectInstanceManager; } @Override public void setResourceManager(ResourceManager resourceManager) { this.resourceManager = resourceManager; } @Override public void setSecurityManager(SecurityManager securityManager) { this.securityManager = securityManager; } @Override public void setServletRequestManager(ServletRequestManager servletRequestManager) { this.servletRequestManager = servletRequestManager; } @Override public void setWelcomeFileManager(WelcomeFileManager welcomeFileManager) { this.welcomeFileManager = welcomeFileManager; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplicationOutputStream.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplicationOutputStream; import cloud.piranha.core.api.WebApplicationResponse; import jakarta.servlet.WriteListener; import jakarta.servlet.http.Cookie; import static jakarta.servlet.http.HttpServletResponse.SC_SWITCHING_PROTOCOLS; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.lang.System.Logger; import static java.lang.System.Logger.Level.TRACE; import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Iterator; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * The default WebApplicationOutputStream. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultWebApplicationOutputStream extends WebApplicationOutputStream implements Runnable { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(DefaultWebApplicationOutputStream.class.getName()); /** * Stores the buffer. */ protected byte[] buffer; /** * Stores the closed flag. */ protected boolean closed; /** * Stores the index. */ protected int index; /** * Stores the web application response. */ protected WebApplicationResponse response; /** * Stores the output stream. */ protected OutputStream outputStream; /** * Stores the write listener. */ protected WriteListener writeListener; /** * Stores the write listener lock. */ protected Lock writeListenerLock = new ReentrantLock(); /** * Constructor. */ public DefaultWebApplicationOutputStream() { this.buffer = new byte[8192]; this.outputStream = new ByteArrayOutputStream(); } @Override public void close() throws IOException { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Closing input stream"); } if (!response.isCommitted()) { response.flushBuffer(); outputStream.flush(); } closed = true; } @Override public void flush() throws IOException { if (response.isBufferResetting()) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Buffer resetting, ignoring flush"); } return; } if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Flushing the input stream"); } if (!response.isCommitted()) { response.flushBuffer(); } outputStream.flush(); } /** * Flush the buffer. * *

* This will cause the following to be written out in order: *

*
    *
  1. the status line
  2. *
  3. the response headers
  4. *
  5. the buffer (aka the response body)
  6. *
* * @throws IOException when an I/O error occurs. */ @Override public void flushBuffer() throws IOException { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Flushing the buffer"); } if (!response.isBodyOnly()) { writeStatusLine(); writeHeaders(); } if (!response.isCommitted()) { outputStream.write(buffer, 0, index); index = buffer.length; response.setCommitted(true); } } /** * Format the timestamp to a GMT string. * * @param timestamp the timestamp. * @return the GMT string. */ private String formatDateToGMT(long timestamp) { return Instant.ofEpochMilli(timestamp).atZone(ZoneId.of("GMT")) .format(DateTimeFormatter.RFC_1123_DATE_TIME); } @Override public int getBufferSize() { return buffer.length; } @Override public OutputStream getOutputStream() { return outputStream; } @Override public WriteListener getWriteListener() { return writeListener; } @Override public WebApplicationResponse getResponse() { return response; } @Override public boolean isReady() { try { writeListenerLock.lock(); return !closed; } finally { writeListenerLock.unlock(); } } @Override public void resetBuffer() { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Reset the buffer"); } this.buffer = new byte[buffer.length]; } @Override public void run() { while (true) { try { if (isReady()) { try { writeListenerLock.lock(); writeListener.onWritePossible(); } finally { writeListenerLock.unlock(); } } if (closed) { break; } try { Thread.sleep(500); } catch (InterruptedException ie) { Thread.currentThread().interrupt();; } } catch (IOException ioe) { writeListener.onError(ioe); break; } } } @Override public void setBufferSize(int bufferSize) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Setting buffer size: {0}", bufferSize); } this.buffer = new byte[bufferSize]; } @Override public void setOutputStream(OutputStream outputStream) { this.outputStream = outputStream; } @Override public void setResponse(WebApplicationResponse response) { this.response = response; } @Override public void setWriteListener(WriteListener writeListener) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Setting WriteListener: {0}", writeListener); } if (writeListener == null) { throw new NullPointerException("Write listener cannot be null"); } if (this.writeListener != null) { throw new IllegalStateException("Write listener can only be set once"); } if (!response.getWebApplication().getRequest(response).isAsyncStarted() && response.getStatus() != SC_SWITCHING_PROTOCOLS) { throw new IllegalStateException("Write listener cannot be set as the request is not upgraded nor async is started"); } this.writeListener = writeListener; Thread thread = new Thread(this); thread.start(); } @Override public void write(int integer) throws IOException { if (writeListener == null) { /* * If the response is in buffer resetting mode, we are going to * ignore any writes to the underlying output stream until the * response is no longer in buffer resetting mode. */ if (response.isBufferResetting()) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Buffer is resetting, ignoring write"); } return; } /* * Servlet:SPEC:192.2 * * If the integer we are looking at will cause the buffer to * overflow, write out the buffer and then write the integer * directly to the underlying output stream. */ if (index == buffer.length - 1) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Buffer is full, flushing"); } flushBuffer(); outputStream.write(integer); } else if (index == buffer.length) { /* * Write the integer directly to the underlying output stream as * the buffer was previously flushed. */ outputStream.write(integer); } else if (index > buffer.length) { /* * The buffer is full, so we need to write the integer directly * to the underlying output stream. */ outputStream.write(integer); } else { /* * Add the integer to the buffer. */ this.buffer[index] = (byte) integer; this.index++; } } else { /* * WriteListener is set so write directly to output stream. */ outputStream.write(integer); } } /** * Write out the content language. * * @throws IOException when an I/O error occurs. */ private void writeContentLanguage() throws IOException { if (response.getContentLanguage() == null) { return; } if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Writing Content-Language: {0}", response.getContentLanguage()); } outputStream.write("Content-Language: ".getBytes()); outputStream.write(response.getContentLanguage().getBytes()); outputStream.write("\n".getBytes()); } /** * Write out the content type. * * @throws IOException when an I/O error occurs. */ private void writeContentType() throws IOException { if (response.getContentType() != null) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Writing Content-Type: {0}", response.getContentType()); } outputStream.write("Content-Type: ".getBytes()); outputStream.write(response.getContentType().getBytes()); /* * Add the character encoding if it is not already there. */ if (response.getCharacterEncoding() != null && !response.getContentType().contains("charset")) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Writing Content-Type Charset: {0}", response.getCharacterEncoding()); } outputStream.write(";charset=".getBytes()); outputStream.write(response.getCharacterEncoding().getBytes()); } outputStream.write("\n".getBytes()); } } /** * Write out a cookie. * * @param cookie the cookie. * @throws IOException when an I/O error occurs. */ @SuppressWarnings({"removal"}) private void writeCookie(Cookie cookie) throws IOException { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Writing Cookie Name: {0}", cookie.getName()); } outputStream.write("Set-Cookie: ".getBytes()); outputStream.write(cookie.getName().getBytes()); outputStream.write("=".getBytes()); if (cookie.getValue() != null) { outputStream.write(cookie.getValue().getBytes()); if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Writing Cookie value: {0}", cookie.getValue()); } } if (cookie.getMaxAge() > -1) { outputStream.write(("; Max-Age=" + cookie.getMaxAge()).getBytes()); String expireDate = formatDateToGMT(Instant.now().plusSeconds(cookie.getMaxAge()).toEpochMilli()); outputStream.write(("; Expires=" + expireDate).getBytes()); if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Writing Cookie Max-Age: {0}", cookie.getMaxAge()); LOGGER.log(TRACE, "Writing Cookie Expires: {0}", expireDate); } } if (cookie.getSecure()) { outputStream.write("; Secure".getBytes()); if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Writing Cookie Secure: {0}", cookie.getSecure()); } } if (cookie.isHttpOnly()) { outputStream.write("; HttpOnly".getBytes()); if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Writing Cookie HttpOnly: {0}", cookie.isHttpOnly()); } } if (cookie.getPath() != null) { outputStream.write(("; Path=" + cookie.getPath()).getBytes()); if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Writing Cookie Path: {0}", cookie.isHttpOnly()); } } if (cookie.getVersion() > 0) { outputStream.write(("; Version=" + cookie.getVersion()).getBytes()); if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Writing Cookie Version: {0}", cookie.getVersion()); } } outputStream.write("\n".getBytes()); } /** * Write out the cookies. * * @throws IOException when an I/O error occurs. */ private void writeCookies() throws IOException { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Writing cookies"); } for (Cookie cookie : response.getCookies()) { writeCookie(cookie); } } /** * Write out a header. * * @param name the name of the header. * @throws IOException when an I/O error occurs. */ private void writeHeader(String name) throws IOException { Iterator values = response.getHeaders(name).iterator(); outputStream.write(name.getBytes()); outputStream.write(": ".getBytes()); while (values.hasNext()) { String value = values.next(); if (value != null) { outputStream.write(value.getBytes()); if (values.hasNext()) { outputStream.write(",".getBytes()); } if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Writing header: {0} with value: {1}", name, value); } } } outputStream.write("\n".getBytes()); } /** * Write out the headers. * * @throws IOException when an I/O error occurs. */ public void writeHeaders() throws IOException { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Writing headers"); } writeContentType(); writeContentLanguage(); writeCookies(); for (String name : response.getHeaderNames()) { writeHeader(name); } outputStream.write("\n".getBytes()); } /** * Write out the status line. * * @throws IOException when an I/O error occurs. */ public void writeStatusLine() throws IOException { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Writing status line: {0}", "HTTP/1.1 " + Integer.toString(response.getStatus())); } outputStream.write("HTTP/1.1".getBytes()); outputStream.write(" ".getBytes()); outputStream.write(Integer.toString(response.getStatus()).getBytes()); if (response.getStatusMessage() != null) { outputStream.write(" ".getBytes()); outputStream.write(response.getStatusMessage().getBytes()); } outputStream.write("\n".getBytes()); } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplicationPrintWriter.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplicationPrintWriter; import cloud.piranha.core.api.WebApplicationResponse; import java.io.IOException; import java.io.Writer; /** * The PrintWriter for a WebApplicationResponse. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultWebApplicationPrintWriter extends WebApplicationPrintWriter { /** * Stores the content length. */ private long contentLength = -1; /** * Stores the content written. */ private long contentWritten = 0; /** * Constructor. * * @param response the response object. * @param writer the writer. * @param autoFlush the auto flush flag. */ public DefaultWebApplicationPrintWriter(WebApplicationResponse response, Writer writer, boolean autoFlush) { super(response, writer, autoFlush); contentLength = response.getContentLength() * 2; } @Override public void write(char[] characters) { if (characters != null) { for (int i = 0; i < characters.length; i++) { write(characters[i]); } } } @Override public void write(String s) { if (s != null) { s.chars().forEach(c -> write(c)); } } @Override public void write(int c) { super.write(c); if (contentLength > 0) { contentWritten++; if (contentWritten > contentLength) { try { response.flushBuffer(); } catch (IOException ioe) { ioe.printStackTrace(); } } } } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplicationRequest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.AttributeManager; import cloud.piranha.core.api.HttpHeaderManager; import cloud.piranha.core.api.HttpSessionManager; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationInputStream; import cloud.piranha.core.api.WebApplicationRequest; import static cloud.piranha.core.impl.DefaultServletRequestDispatcher.PREVIOUS_REQUEST; import jakarta.servlet.AsyncContext; import jakarta.servlet.DispatcherType; import static jakarta.servlet.DispatcherType.INCLUDE; import jakarta.servlet.MultipartConfigElement; import jakarta.servlet.ReadListener; import jakarta.servlet.RequestDispatcher; import static jakarta.servlet.RequestDispatcher.INCLUDE_QUERY_STRING; import jakarta.servlet.ServletConnection; import jakarta.servlet.ServletException; import jakarta.servlet.ServletInputStream; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletRequestWrapper; import jakarta.servlet.ServletResponse; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletMapping; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpUpgradeHandler; import jakarta.servlet.http.Part; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.lang.System.Logger; import static java.lang.System.Logger.Level.WARNING; import java.lang.reflect.InvocationTargetException; import java.net.URLDecoder; import java.nio.charset.Charset; import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.StandardCharsets; import static java.nio.charset.StandardCharsets.UTF_8; import java.nio.file.Path; import java.nio.file.Paths; import java.security.Principal; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Locale; import java.util.Map; import static java.util.Objects.requireNonNull; import java.util.UUID; /** * The default WebApplicationRequest. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultWebApplicationRequest implements WebApplicationRequest { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(DefaultWebApplicationRequest.class.getName()); /** * Defines the 'multipart/form-data' constant. */ private static final String MULTIPART_FORM_DATA = "multipart/form-data"; /** * Stores the async context. */ protected AsyncContext asyncContext; /** * Stores if async is started. */ protected boolean asyncStarted; /** * Stores if async is supported. */ protected boolean asyncSupported; /** * Stores the attribute manager. */ protected AttributeManager attributeManager; /** * Stores the auth type. */ protected String authType; /** * Stores the character encoding. */ protected String characterEncoding; /** * Stores the content length. */ protected long contentLength; /** * Stores the content type. */ protected String contentType; /** * Stores the context path. */ protected String contextPath; /** * Stores the cookies. */ protected Cookie[] cookies; /** * Stores the current session id. */ protected String currentSessionId; /** * Stores the dispatcher type. */ protected DispatcherType dispatcherType; /** * Stores the gotInputStream flag. */ protected boolean gotInputStream; /** * Stores the gotReader flag. */ protected boolean gotReader; /** * Stores the header manager. */ protected HttpHeaderManager headerManager; /** * Stores the httpServletMapping. */ protected HttpServletMapping httpServletMapping; /** * Stores the number of items read from the input stream */ protected int index; /** * Stores the input stream. */ protected WebApplicationInputStream webApplicationInputStream; /** * Stores the local address. */ protected String localAddress; /** * Stores the local name. */ protected String localName; /** * Stores the local port. */ protected int localPort; /** * Stores the method. */ protected String method; /** * Stores the multipart config. */ protected MultipartConfigElement multipartConfig; /** * Stores the original servlet path. */ protected String originalServletPath; /** * Stores the parameters. */ protected HashMap parameters; /** * Stores the parameters parsed flag. */ protected boolean parametersParsed; /** * Stores the path info. */ protected String pathInfo; /** * Stores the protocol. */ protected String protocol; /** * Stores the protocol request id. */ protected String protocolRequestId; /** * Stores the query string. */ protected String queryString; /** * Stores the read listener. */ protected ReadListener readListener; /** * Stores the reader. */ protected BufferedReader reader; /** * Stores the remote address. */ protected String remoteAddr; /** * Stores the remote host. */ protected String remoteHost; /** * Stores the remote port. */ protected int remotePort; /** * Stores the request id. */ protected String requestId; /** * Stores the requested session id. */ protected String requestedSessionId; /** * Stores the requested session id from cookie flag. */ protected boolean requestedSessionIdFromCookie; /** * Stores the requested session id from url flag. */ protected boolean requestedSessionIdFromURL; /** * Stores the scheme. */ protected String scheme; /** * Stores the server name. */ protected String serverName; /** * Stores the server port. */ protected int serverPort; /** * Stores the servlet connection. */ protected ServletConnection servletConnection; /** * Stores the servlet path. */ protected String servletPath; /** * Stores the upgrade handler. */ protected HttpUpgradeHandler upgradeHandler; /** * Stores the upgraded flag. */ protected boolean upgraded; /** * Stores the user principal. */ protected Principal userPrincipal; /** * Stores the web application */ protected WebApplication webApplication; /** * Constructor. */ public DefaultWebApplicationRequest() { this.authType = null; this.asyncStarted = false; this.asyncSupported = false; this.attributeManager = new DefaultAttributeManager(); this.characterEncoding = null; this.contentLength = -1; this.contentType = null; this.contextPath = ""; this.cookies = null; this.dispatcherType = DispatcherType.REQUEST; this.headerManager = new DefaultHttpHeaderManager(); this.headerManager.setHeader("Accept", "*/*"); this.webApplicationInputStream = new DefaultWebApplicationInputStream(); this.webApplicationInputStream.setWebApplicationRequest(this); this.method = "GET"; this.protocol = "HTTP/1.1"; this.protocolRequestId = ""; this.requestId = UUID.randomUUID().toString(); this.scheme = "http"; this.serverName = "localhost"; this.serverPort = 80; this.servletPath = ""; this.servletConnection = new DefaultServletConnection(); this.parameters = new HashMap<>(); this.upgraded = false; } /** * Add the header. * * @param name the name. * @param value the value (string). */ public void addHeader(String name, String value) { headerManager.addHeader(name, value); } /** * Add or remove slash if needed. * * @param string the string the look at. * @return the sanitized string. */ private String addOrRemoveSlashIfNeeded(String string) { if (string.startsWith("/")) { if (string.startsWith("//")) { return string.substring(1); } return string; } return "/" + string; } @Override public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { boolean authenticated = false; if (webApplication.getManager().getSecurityManager() != null) { authenticated = webApplication.getManager().getSecurityManager().authenticate(this, response); } return authenticated; } @Override public String changeSessionId() { if (webApplication.getManager().getHttpSessionManager() != null) { currentSessionId = webApplication.getManager().getHttpSessionManager().changeSessionId(this); } return currentSessionId; } @SafeVarargs private T coalesce(T... objects) { for (T object : objects) { if (object != null) { return object; } } return null; } @Override public AsyncContext getAsyncContext() { if (asyncContext == null) { throw new IllegalStateException("Async was not started"); } return asyncContext; } @Override public Object getAttribute(String name) { return attributeManager.getAttribute(name); } @Override public Enumeration getAttributeNames() { return attributeManager.getAttributeNames(); } @Override public String getAuthType() { return authType; } @Override public String getCharacterEncoding() { return characterEncoding; } @Override public int getContentLength() { return (int) contentLength; } @Override public long getContentLengthLong() { return contentLength; } @Override public String getContentType() { return contentType; } @Override public String getContextPath() { return contextPath; } @Override public Cookie[] getCookies() { Cookie[] result = null; if (cookies != null) { result = new Cookie[cookies.length]; for (int i = 0; i < result.length; i++) { result[i] = (Cookie) cookies[i].clone(); } } return result; } /** * {@return the current session id} */ public String getCurrentSessionId() { return currentSessionId; } @Override public long getDateHeader(String name) { return headerManager.getDateHeader(name); } @Override public DispatcherType getDispatcherType() { return dispatcherType; } @Override public String getHeader(String name) { return headerManager.getHeader(name); } @Override public Enumeration getHeaderNames() { return headerManager.getHeaderNames(); } @Override public Enumeration getHeaders(String name) { return headerManager.getHeaders(name); } @Override public HttpServletMapping getHttpServletMapping() { if (httpServletMapping == null) { return WebApplicationRequest.super.getHttpServletMapping(); } return httpServletMapping; } @Override public ServletInputStream getInputStream() throws IOException { ServletInputStream result; if (!gotReader) { gotInputStream = true; result = webApplicationInputStream; } else { throw new IllegalStateException( "Cannot getInputStream because getReader has been previously called"); } return result; } @Override public int getIntHeader(String name) { return headerManager.getIntHeader(name); } @Override public String getLocalAddr() { return localAddress; } @Override public String getLocalName() { return localName; } @Override public int getLocalPort() { return localPort; } @SuppressWarnings("deprecation") @Override public Locale getLocale() { Locale result = Locale.getDefault(); Enumeration languages = getHeaders("Accept-Language"); if (languages.hasMoreElements()) { String localeString = languages.nextElement(); String[] localeStrings = localeString.split(","); if (localeStrings[0].contains("-")) { String[] localeString1 = localeStrings[0].split("-"); result = new Locale(localeString1[0].trim(), localeString1[1].trim()); } else { result = new Locale(localeStrings[0].trim()); } } return result; } @SuppressWarnings("deprecation") @Override public Enumeration getLocales() { ArrayList locales = new ArrayList<>(); Enumeration languages = getHeaders("Accept-Language"); if (languages.hasMoreElements()) { String localeString = languages.nextElement(); String[] localeStrings = localeString.split(","); for (String localeString1 : localeStrings) { if (localeString1.contains("-")) { String[] localeString2 = localeString1.split("-"); locales.add(new Locale(localeString2[0].trim(), localeString2[1].trim())); } else { locales.add(new Locale(localeString1.trim())); } } } else { locales = new ArrayList<>(); locales.add(Locale.getDefault()); } return Collections.enumeration(locales); } @Override public String getMethod() { return method; } @Override public Map getModifiableParameterMap() { return parameters; } @Override public MultipartConfigElement getMultipartConfig() { return multipartConfig; } /** * Gets the original Servlet Path * * @return the original Servlet Path */ public String getOriginalServletPath() { return originalServletPath; } @Override public String getParameter(String name) { String result = null; getParametersFromRequest(); if (getParameterValues(name) != null) { result = getParameterValues(name)[0]; } return result; } @Override public Map getParameterMap() { getParametersFromRequest(); return Collections.unmodifiableMap(parameters); } @Override public Enumeration getParameterNames() { getParametersFromRequest(); return Collections.enumeration(parameters.keySet()); } @Override public String[] getParameterValues(String name) { getParametersFromRequest(); return parameters.get(name); } /** * Get the parameters from the request. */ protected void getParametersFromRequest() { if (!parametersParsed) { parametersParsed = true; try { String mergedQueryString = mergeQueryFromAttributes(); if (mergedQueryString != null) { for (String param : mergedQueryString.split("&")) { String[] pair = param.split("="); String key = URLDecoder.decode(pair[0], UTF_8); String value = ""; if (pair.length > 1) { value = URLDecoder.decode(pair[1], UTF_8); } String[] values = parameters.get(key); if (values == null) { values = new String[]{value}; parameters.put(key, values); } else { String[] newValues = new String[values.length + 1]; System.arraycopy(values, 0, newValues, 0, values.length); newValues[values.length] = value; parameters.put(key, newValues); } } } boolean hasMultiPart = // FORM/Multipart submission contentType != null && contentType.startsWith(MULTIPART_FORM_DATA); if (hasMultiPart) { for (Part part : getParts()) { if (part.getSubmittedFileName() == null) { setParameter(part.getName(), new String[]{new String(part.getInputStream().readAllBytes())}); } } } else { boolean hasBody = // FORM submission contentType != null && contentType.startsWith("application/x-www-form-urlencoded") || // PUT parameters "put".equalsIgnoreCase(getMethod()) && getContentLength() > 0; if (hasBody) { ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); int read = webApplicationInputStream.read(); while (read != -1) { byteOutput.write(read); read = webApplicationInputStream.read(); } if (read != -1) { byteOutput.write(read); } String parameterString = new String(byteOutput.toByteArray()); String[] pairs = parameterString.trim().split("&"); if (pairs != null) { for (int i = 0; i < pairs.length; i++) { String[] pair = pairs[i].trim().split("="); if (pair.length == 2) { pair[0] = URLDecoder.decode(pair[0], UTF_8); pair[1] = URLDecoder.decode(pair[1], UTF_8); setParameter(pair[0], new String[]{pair[1]}); } else { pair[0] = URLDecoder.decode(pair[0], UTF_8); if (!"".equals(pair[0])) { setParameter(pair[0], new String[]{""}); } } } } } } } catch (IOException | ServletException ioe) { throw new RuntimeException(ioe); } } } @Override public Part getPart(String name) throws IOException, ServletException { verifyMultipartFormData(); return webApplication.getManager().getMultiPartManager() .getPart(webApplication, this, name); } @Override public Collection getParts() throws IOException, ServletException { verifyMultipartFormData(); return webApplication.getManager().getMultiPartManager() .getParts(webApplication, this); } @Override public String getPathInfo() { return pathInfo; } @Override public String getPathTranslated() { return null; } @Override public String getProtocol() { return protocol; } @Override public String getProtocolRequestId() { return protocolRequestId; } @Override public String getQueryString() { return queryString; } @Override public BufferedReader getReader() throws IOException { if (!gotInputStream) { if (reader == null) { String charsetName = characterEncoding == null ? StandardCharsets.ISO_8859_1.toString() : characterEncoding; if (!isSupported(charsetName)) { throw new UnsupportedEncodingException(charsetName); } reader = new BufferedReader(new InputStreamReader(webApplicationInputStream, charsetName)); } gotReader = true; } else { throw new IllegalStateException("Cannot getReader because getInputStream has been previously called"); } return reader; } @Override public String getRemoteAddr() { return remoteAddr; } @Override public String getRemoteHost() { return remoteHost; } @Override public int getRemotePort() { return remotePort; } @Override public String getRemoteUser() { String result = null; if (getUserPrincipal() != null) { result = getUserPrincipal().getName(); } return result; } @Override public RequestDispatcher getRequestDispatcher(String path) { Path rootContext = Paths.get(getContextPath()); Path resolved = rootContext.resolveSibling(Paths.get(path)).normalize(); if (!resolved.startsWith(rootContext)) { resolved = rootContext.resolveSibling(resolved); } String servletPath = resolved.toString().replace('\\', '/'); return webApplication.getRequestDispatcher(servletPath); } @Override public String getRequestId() { return requestId; } @Override public String getRequestURI() { return addOrRemoveSlashIfNeeded( contextPath + coalesce(originalServletPath, servletPath) + coalesce(pathInfo, "")); } /** * {@return the request URI with query string} */ public String getRequestURIWithQueryString() { String requestURI = getRequestURI(); String queryString = getQueryString(); return queryString == null ? requestURI : requestURI + "?" + queryString; } @Override public StringBuffer getRequestURL() { StringBuffer result = new StringBuffer(); result.append(getScheme()); result.append("://"); result.append(getServerName()); result.append(":"); result.append(getServerPort()); result.append(getRequestURI()); return result; } @Override public String getRequestedSessionId() { return requestedSessionId; } @Override public String getScheme() { return scheme; } @Override public String getServerName() { return serverName; } @Override public int getServerPort() { return serverPort; } @Override public ServletConnection getServletConnection() { return servletConnection; } @Override public WebApplication getServletContext() { return webApplication; } @Override public String getServletPath() { return servletPath; } @Override public HttpSession getSession() { return getSession(true); } @Override public HttpSession getSession(boolean create) { if (webApplication == null || webApplication.getManager().getHttpSessionManager() == null) { return null; } HttpSession session = null; HttpSessionManager manager = webApplication.getManager().getHttpSessionManager(); if (currentSessionId == null && requestedSessionId != null) { currentSessionId = requestedSessionId; } if (manager.hasSession(currentSessionId)) { session = manager.getSession(this, currentSessionId); } else if (create) { session = manager.createSession(this); currentSessionId = session.getId(); } return session; } @Override public HttpUpgradeHandler getUpgradeHandler() { return upgradeHandler; } @Override public Principal getUserPrincipal() { return userPrincipal; } @Override public WebApplicationInputStream getWebApplicationInputStream() { return webApplicationInputStream; } @Override public boolean isAsyncStarted() { return asyncStarted; } @Override public boolean isAsyncSupported() { return asyncSupported; } @Override public boolean isRequestedSessionIdFromCookie() { return requestedSessionIdFromCookie; } @Override public boolean isRequestedSessionIdFromURL() { return requestedSessionIdFromURL; } @Override public boolean isRequestedSessionIdValid() { boolean result = false; if (requestedSessionId != null && webApplication.getManager().getHttpSessionManager() != null) { HttpSessionManager manager = webApplication.getManager().getHttpSessionManager(); result = manager.hasSession(requestedSessionId); } return result; } @Override public boolean isSecure() { return "https".equals(scheme); } private boolean isSupported(String csn) { try { return Charset.isSupported(csn); } catch (IllegalCharsetNameException x) { return false; } } @Override public boolean isUpgraded() { return upgraded; } @Override public boolean isUserInRole(String role) { boolean userInRole = false; if (webApplication.getManager().getSecurityManager() != null) { userInRole = webApplication.getManager().getSecurityManager().isUserInRole(this, role); } return userInRole; } @Override public void login(String username, String password) throws ServletException { if (webApplication.getManager().getSecurityManager() != null) { webApplication.getManager().getSecurityManager().login(this, username, password); } else { throw new ServletException("No security manager configured"); } } @Override public void logout() throws ServletException { if (webApplication.getManager().getSecurityManager() != null) { webApplication.getManager().getSecurityManager().logout(this, (HttpServletResponse) webApplication.getResponse(this)); } } /** * Merge query string from this request and from the attribute * {@link RequestDispatcher#INCLUDE_QUERY_STRING} if the dispatcher type is * {@link DispatcherType#INCLUDE} * * @return the query string merged */ private String mergeQueryFromAttributes() { String queryStringFromAttribute = dispatcherType == INCLUDE ? (String) getAttribute(INCLUDE_QUERY_STRING) : null; if (queryStringFromAttribute == null) { return queryString; } if (queryString == null) { return queryStringFromAttribute; } return queryStringFromAttribute + "&" + queryString; } @Override public void removeAttribute(String name) { Object oldValue = attributeManager.getAttribute(name); attributeManager.removeAttribute(name); if (webApplication != null && webApplication.getManager().getServletRequestManager() != null) { webApplication.getManager().getServletRequestManager().attributeRemoved(this, name, oldValue); } } /** * Set the async started flag. * * @param asyncStarted the async started flag. */ public void setAsyncStarted(boolean asyncStarted) { this.asyncStarted = asyncStarted; } @Override public void setAsyncSupported(boolean asyncSupported) { this.asyncSupported = asyncSupported; } @Override public void setAttribute(String name, Object value) { if (value != null) { boolean added = true; Object oldValue = attributeManager.getAttribute(name); if (oldValue == null) { added = false; } attributeManager.setAttribute(name, value); if (webApplication != null && webApplication.getManager().getServletRequestManager() != null) { if (!added) { webApplication.getManager().getServletRequestManager().attributeAdded(this, name, value); } else { webApplication.getManager().getServletRequestManager().attributeReplaced(this, name, oldValue); } } } else { Object oldValue = attributeManager.getAttribute(name); attributeManager.removeAttribute(name); if (webApplication != null && webApplication.getManager().getServletRequestManager() != null) { webApplication.getManager().getServletRequestManager().attributeRemoved(this, name, oldValue); } } } @Override public void setAuthType(String authType) { this.authType = authType; } @Override public void setCharacterEncoding(String characterEncoding) throws UnsupportedEncodingException { boolean supported = false; try { supported = Charset.isSupported(characterEncoding); } catch (IllegalArgumentException iae) { LOGGER.log(WARNING, "Illegal argument for setting character encoding", iae); } if (!supported) { throw new UnsupportedEncodingException("Character encoding '" + characterEncoding + "' is not supported"); } if (!gotReader) { this.characterEncoding = characterEncoding; } } /** * Set the content length. * * @param contentLength the content length. */ public void setContentLength(int contentLength) { this.contentLength = contentLength; headerManager.setHeader("Content-Length", Integer.toString(contentLength)); } /** * Set the content type. * * @param contentType the content type. */ public void setContentType(String contentType) { this.contentType = contentType; headerManager.setHeader("Content-Type", contentType); if (contentType.startsWith(MULTIPART_FORM_DATA)) { // "multipart/form-data" contains a boundary and no charset return; } String[] parts = contentType.split(";"); if (parts.length == 1) { return; } String charset = parts[1].trim(); String[] pair = charset.split("="); if (pair.length == 1) { return; } characterEncoding = pair[1].trim(); } @Override public void setContextPath(String contextPath) { this.contextPath = contextPath; } /** * Set the cookies. * * @param cookies the cookies. */ public void setCookies(Cookie[] cookies) { if (cookies == null || cookies.length == 0) { this.cookies = null; } else { this.cookies = new Cookie[cookies.length]; for (int i = 0; i < cookies.length; i++) { this.cookies[i] = (Cookie) cookies[i].clone(); } } } /** * Sets the current session id * * @param currentSessionId the current session id */ public void setCurrentSessionId(String currentSessionId) { this.currentSessionId = currentSessionId; } @Override public void setDispatcherType(DispatcherType dispatcherType) { this.dispatcherType = dispatcherType; } /** * Set the header. * * @param name the name. * @param value the value (string). */ public void setHeader(String name, String value) { if (name.equalsIgnoreCase("content-type")) { setContentType(value); } else if (name.equalsIgnoreCase("content-length")) { try { setContentLength(Integer.parseInt(value)); } catch (NumberFormatException nfe) { LOGGER.log(WARNING, "Unable to setContentLength as header value is unparseable", nfe); } } else { headerManager.setHeader(name, value); } } /** * Set the HTTP servlet mapping. * * @param httpServletMapping the HTTP servlet mapping. */ public void setHttpServletMapping(HttpServletMapping httpServletMapping) { this.httpServletMapping = httpServletMapping; } /** * Set the local address. * * @param localAddress the local address. */ public void setLocalAddr(String localAddress) { this.localAddress = localAddress; } /** * Set the local name. * * @param localName the local name. */ public void setLocalName(String localName) { this.localName = localName; } /** * Set the local port. * * @param localPort the local port. */ public void setLocalPort(int localPort) { this.localPort = localPort; } /** * Set the method. * * @param method the method. */ public void setMethod(String method) { this.method = method; } /** * Set the multipart config. * * @param multipartConfig the multipartConfig. */ public void setMultipartConfig(MultipartConfigElement multipartConfig) { this.multipartConfig = multipartConfig; } /** * Set the original Servlet path * * @param originalServletPath the original Servlet path */ public void setOriginalServletPath(String originalServletPath) { this.originalServletPath = originalServletPath; } /** * Set the parameter values. * * @param name the parameter name. * @param values the values. */ public void setParameter(String name, String[] values) { parameters.put(name, values); } @Override public void setPathInfo(String pathInfo) { this.pathInfo = pathInfo; } /** * Set the protocol. * * @param protocol the protocol. */ public void setProtocol(String protocol) { this.protocol = protocol; } @Override public void setQueryString(String queryString) { this.queryString = queryString; } /** * Set the remote address. * * @param remoteAddr the remote address. */ public void setRemoteAddr(String remoteAddr) { this.remoteAddr = remoteAddr; } /** * Set the remote host. * * @param remoteHost the remote host. */ public void setRemoteHost(String remoteHost) { this.remoteHost = remoteHost; } /** * Set the remote port. * * @param remotePort the remote port. */ public void setRemotePort(int remotePort) { this.remotePort = remotePort; } @Override public void setRequestedSessionId(String requestedSessionId) { this.requestedSessionId = requestedSessionId; } /** * Set the requested session id from cookie. * * @param requestedSessionIdFromCookie the requested session id from cookie. */ public void setRequestedSessionIdFromCookie(boolean requestedSessionIdFromCookie) { this.requestedSessionIdFromCookie = requestedSessionIdFromCookie; } /** * Set the request session id from URL flag. * * @param requestedSessionIdFromURL the requested session if from URL flag. */ public void setRequestedSessionIdFromURL(boolean requestedSessionIdFromURL) { this.requestedSessionIdFromURL = requestedSessionIdFromURL; } /** * Set the scheme. * * @param scheme the scheme. */ public void setScheme(String scheme) { this.scheme = scheme; } /** * Set the server name. * * @param serverName the server name. */ public void setServerName(String serverName) { this.serverName = serverName; } /** * Set the server port. * * @param serverPort the server port. */ public void setServerPort(int serverPort) { this.serverPort = serverPort; } @Override public void setServletPath(String servletPath) { this.servletPath = servletPath; } /** * Set the upgraded flag. * * @param upgraded the upgraded flag. */ public void setUpgraded(boolean upgraded) { this.upgraded = upgraded; } @Override public void setUserPrincipal(Principal userPrincipal) { this.userPrincipal = userPrincipal; } @Override public void setWebApplication(WebApplication webApplication) { this.webApplication = webApplication; } @Override public void setWebApplicationInputStream( WebApplicationInputStream webApplicationInputStream) { this.webApplicationInputStream = webApplicationInputStream; } @Override public AsyncContext startAsync() throws IllegalStateException { if (!isAsyncSupported()) { throw new IllegalStateException("Async is not supported"); } return startAsync(this, this.webApplication.getResponse(this)); } @Override public AsyncContext startAsync(ServletRequest request, ServletResponse response) throws IllegalStateException { requireNonNull(request); requireNonNull(response); if (!isAsyncSupported()) { throw new IllegalStateException("Async is not supported"); } if (request.getAttribute("CALLED_FROM_ASYNC_WRAPPER") != null) { return new DefaultAsyncContext(request, response); } if (asyncContext != null) { throw new IllegalStateException("Async cycle has already been started"); } asyncContext = new DefaultAsyncContext(request, response); asyncStarted = true; Object previousAttribute = request.getAttribute(PREVIOUS_REQUEST); while (previousAttribute instanceof HttpServletRequest httpServletRequest) { HttpServletRequest previousRequest = unwrap(httpServletRequest, HttpServletRequest.class); if (previousRequest instanceof DefaultWebApplicationRequest defaultRequest) { defaultRequest.setAsyncStarted(true); } previousAttribute = previousRequest.getAttribute(PREVIOUS_REQUEST); } return asyncContext; } /** * Unwrap the request. * * @param the type to unwrap to. * @param request the request. * @param type the class type of the result * @return the unwrapped request. */ public static T unwrap(ServletRequest request, Class type) { ServletRequest currentRequest = request; while (currentRequest instanceof ServletRequestWrapper wrapper) { currentRequest = wrapper.getRequest(); } return type.cast(currentRequest); } @SuppressWarnings("unchecked") @Override public T upgrade(Class handlerClass) throws IOException, ServletException { try { upgradeHandler = handlerClass.getDeclaredConstructor().newInstance(); } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException ie) { throw new ServletException(ie); } setUpgraded(true); return (T) upgradeHandler; } /** * Verify the method is of type "multipart/form-data" * * @throws ServletException the exception thrown when it is not. */ protected void verifyMultipartFormData() throws ServletException { if (contentType == null || !contentType.startsWith(MULTIPART_FORM_DATA)) { throw new ServletException("Request not of type multipart/form-data"); } } @Override public String toString() { return getRequestURIWithQueryString(); } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplicationRequestBuilder.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplication; /** * The builder for DefaultWebApplicationRequest instances. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultWebApplicationRequestBuilder { /** * Stores the query string. */ private String queryString; /** * Stores the servlet path. */ private String servletPath; /** * Stores the web application. */ private WebApplication webApplication; /** * Constructor. */ public DefaultWebApplicationRequestBuilder() { } /** * Build the request. * * @return the request. */ public DefaultWebApplicationRequest build() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setQueryString(queryString); request.setServletPath(servletPath != null ? servletPath : ""); request.setWebApplication(webApplication); return request; } /** * Set the query string. * * @param queryString the query string. * @return the builder. */ public DefaultWebApplicationRequestBuilder queryString(String queryString) { this.queryString = queryString; return this; } /** * Set the servlet path. * * @param servletPath the servlet path. * @return the builder. */ public DefaultWebApplicationRequestBuilder servletPath(String servletPath) { this.servletPath = servletPath; return this; } /** * Set the web application. * * @param webApplication the web application. * @return the builder. */ public DefaultWebApplicationRequestBuilder webApplication(WebApplication webApplication) { this.webApplication = webApplication; return this; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplicationRequestMapper.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.FilterMapping; import cloud.piranha.core.api.WebApplicationRequestMapper; import jakarta.servlet.DispatcherType; import static jakarta.servlet.DispatcherType.REQUEST; import java.util.ArrayList; import static java.util.Arrays.stream; import java.util.Collection; import static java.util.Collections.emptySet; import java.util.EnumSet; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import static java.util.Objects.requireNonNull; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import static java.util.stream.Collectors.toSet; /** * The default WebApplicationRequestMapper. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultWebApplicationRequestMapper implements WebApplicationRequestMapper { /** * Stores the filter mappings. */ protected final List filterMappings = new ArrayList<>(); /** * Stores the servlet mappings. */ protected final ConcurrentHashMap servletMappings = new ConcurrentHashMap<>(); /** * Stores the default servlet */ protected String defaultServlet; /** * Constructor. */ public DefaultWebApplicationRequestMapper() { } @Override public Set addFilterMapping(Set dispatcherTypes, String filterName, String... urlPatterns) { return doAddFilterMapping(isEmpty(dispatcherTypes)? EnumSet.of(REQUEST) : dispatcherTypes, filterName, urlPatterns); } @Override public Set addFilterMappingBeforeExisting(Set dispatcherTypes, String filterName, String... urlPatterns) { return doAddFilterMappingBeforeExisting(isEmpty(dispatcherTypes)? EnumSet.of(REQUEST) : dispatcherTypes, filterName, urlPatterns); } @Override public Set addServletMapping(String servletName, String... urlPatterns) { if (isEmpty(urlPatterns)) { throw new IllegalArgumentException("URL patterns for " + servletName + " cannot be empty"); } // Servlet:JAVADOC:696.1 - If any of the specified URL patterns are already mapped to a different Servlet, no updates will be performed. Set mappedToOtherServlets = stream(urlPatterns) .filter(servletMappings::containsKey) .filter(urlPattern -> !servletMappings.get(urlPattern).equals(servletName)) .collect(toSet()); if (!mappedToOtherServlets.isEmpty()) { return mappedToOtherServlets; } for (String urlPattern : urlPatterns) { if ("/".equals(urlPattern)) { // Spec 12.2. Specification of Mappings // "A string containing only the "/" character indicates the "default" servlet of the application" defaultServlet = servletName; } else { if (!urlPattern.startsWith("*") && !urlPattern.startsWith("/")) { urlPattern = "/" + urlPattern; } servletMappings.put(urlPattern, servletName); } } return emptySet(); } @Override public String removeServletMapping(String urlPattern) { return servletMappings.remove(urlPattern); } /** * Find the filter mappings. * * @param path the path. * @return the filter mappings. */ @Override public Collection findFilterMappings(DispatcherType dispatcherType, String path) { List result = new ArrayList<>(); if (path.contains("?")) { path = path.substring(0, path.indexOf("?")); } for (FilterMapping filterMapping : filterMappings) { if (dispatcherType.equals(filterMapping.getDispatcherType())) { String filterName = filterMapping.getFilterName(); String urlPattern = filterMapping.getUrlPattern(); if (path.equals(urlPattern)) { result.add(filterName); } else if (!path.startsWith("servlet:// ")) { // For Servlet "patterns", only do exact matches. // URL patterns are also matched prefix and extension. if (urlPattern.startsWith("*.")) { urlPattern = urlPattern.substring(1); if (path.endsWith(urlPattern)) { result.add(filterName); } } else if (!urlPattern.startsWith("*.") && urlPattern.endsWith("/*")) { urlPattern = urlPattern.substring(0, urlPattern.length() - 2); if (path.startsWith(urlPattern)) { result.add(filterName); } } } } } return result; } private Set doAddFilterMapping(Set dispatcherTypes, String filterName, String... urlPatterns) { requireNonNull(dispatcherTypes); Set result = new HashSet<>(); for (String urlPattern : urlPatterns) { for (DispatcherType dispatcherType : dispatcherTypes) { DefaultFilterMapping filterMapping = new DefaultFilterMapping(dispatcherType, filterName, urlPattern); if (filterMappings.contains(filterMapping)) { result.add(urlPattern); } else { filterMappings.add(filterMapping); } } } return result; } private Set doAddFilterMappingBeforeExisting(Set dispatcherTypes, String filterName, String... urlPatterns) { requireNonNull(dispatcherTypes); Set result = new HashSet<>(); for (String urlPattern : urlPatterns) { for (DispatcherType dispatcherType : dispatcherTypes) { DefaultFilterMapping filterMapping = new DefaultFilterMapping(dispatcherType, filterName, urlPattern); if (filterMappings.contains(filterMapping)) { result.add(urlPattern); } else { filterMappings.add(0, filterMapping); } } } return result; } /** * Find a servlet mapping with an exact mapping. * * @param path the path. * @return the mapping, or null if not found. */ private DefaultWebApplicationRequestMapping findServletExactMatch(String path) { DefaultWebApplicationRequestMapping result = null; Enumeration exacts = servletMappings.keys(); while (exacts.hasMoreElements()) { String exact = exacts.nextElement(); if (path.equals(exact)) { result = new DefaultWebApplicationRequestMapping(exact); result.setExact(true); result.setMatchValue(exact.substring(1)); break; } } return result; } /** * Find a servlet mapping with an extension match. * * @param path the path. * @return the mapping, or null if not found. */ private DefaultWebApplicationRequestMapping findServletExtensionMatch(String path) { DefaultWebApplicationRequestMapping result = null; Enumeration extensions = servletMappings.keys(); while (extensions.hasMoreElements()) { String extension = extensions.nextElement(); /* * Make sure it really is an extension first. */ if (extension.startsWith("*.")) { extension = extension.substring(1); if (path.endsWith(extension)) { result = new DefaultWebApplicationRequestMapping("*" + extension); result.setExtension(true); // If path is /foo.bar and the initial extension is *.bar, then // the match value is foo. result.setMatchValue(path.substring(1, path.lastIndexOf(extension))); break; } } } return result; } /** * Find a servlet mapping for the given path. * * @param path the path. * @return the mapping, or null if not found. */ @Override public DefaultWebApplicationRequestMapping findServletMapping(String path) { if (path.contains("?")) { path = path.substring(0, path.indexOf("?")); } DefaultWebApplicationRequestMapping result = findServletExactMatch(path); if (result == null) { result = findServletPrefixMatch(path); } if (result == null) { result = findServletExtensionMatch(path); } return result; } /** * Find a servlet mapping with the longest prefix mapping. * * @param path the path. * @return the mapping, or null if not found. */ private DefaultWebApplicationRequestMapping findServletPrefixMatch(String path) { DefaultWebApplicationRequestMapping result = null; DefaultWebApplicationRequestMapping found; for (;;) { found = findServletPrefixMatch(path, result); if (found != null) { result = found; } else { break; } } if (result != null) { result.setPattern(result.getPattern() + "*"); } return result; } /** * Find a servlet mapping with a prefix mapping longer than the given * current prefix. * * @param path the path. * @param currentPrefix the current matched prefix. * @return the mapping, or null if not found. */ private DefaultWebApplicationRequestMapping findServletPrefixMatch(String path, DefaultWebApplicationRequestMapping currentPrefix) { DefaultWebApplicationRequestMapping result = null; Enumeration prefixes = servletMappings.keys(); while (prefixes.hasMoreElements()) { String prefix = prefixes.nextElement(); if (!prefix.startsWith("*.") && prefix.endsWith("/*")) { prefix = prefix.substring(0, prefix.length() - 1); if ((path + "/").startsWith(prefix)) { result = new DefaultWebApplicationRequestMapping(prefix); // If path is /foo/bar and the initial prefix is /foo/* then the // match value is bar result.setMatchValue(path.substring(1)); break; } } } if (result != null && currentPrefix != null && result.getPattern().length() <= currentPrefix.getPattern().length()) { result = null; } return result; } /** * Get the mappings for the specified servlet. * * @param servletName the servlet name. * @return the mappings, or an empty collection if none. */ @Override public Collection getServletMappings(String servletName) { Collection result = new ArrayList<>(); servletMappings.keySet().stream().filter(urlPattern -> servletMappings.get(urlPattern).equals(servletName)).forEach(result::add); return result; } /** * Get the servlet name for the specified mapping.. * * @param mapping the mapping. * @return the servlet name, or null if not found. */ @Override public String getServletName(String mapping) { return servletMappings.get(mapping); } @Override public String getDefaultServlet() { return defaultServlet; } private boolean isEmpty(Collection collection) { return collection == null || collection.isEmpty(); } private boolean isEmpty(String[] strings) { return strings == null || strings.length == 0; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplicationRequestMapping.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplicationRequestMapping; /** * The default WebApplicationRequestMapping. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultWebApplicationRequestMapping implements WebApplicationRequestMapping { /** * Stores the getMatchValue. */ private String matchValue; /** * Stores the exact flag. */ private boolean exact; /** * Stores the extension flag. */ private boolean extension; /** * Stores the pattern. */ private String pattern; /** * Constructor. * * @param pattern the pattern. */ public DefaultWebApplicationRequestMapping(String pattern) { this.pattern = pattern; } /** * {@return the matchValue} */ @Override public String getMatchValue() { return matchValue; } /** * {@return the pattern} */ @Override public String getPattern() { return pattern; } /** * Is this an exact match. * * @return true it it is, false otherwise. */ @Override public boolean isExact() { return exact; } /** * Is this an extension match. * * @return true if it is, false otherwise. */ @Override public boolean isExtension() { return extension; } /** * Set the exact flag. * * @param exact the exact flag. */ public void setExact(boolean exact) { this.exact = exact; } /** * Set the extension flag. * * @param extension the extension flag. */ public void setExtension(boolean extension) { this.extension = extension; } /** * Set the matchValue. * * @param matchValue the matchValue to set */ public void setMatchValue(String matchValue) { this.matchValue = matchValue; } /** * Set the pattern. * * @param pattern the pattern. */ public void setPattern(String pattern) { this.pattern = pattern; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplicationResponse.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.LocaleEncodingManager; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationResponse; import cloud.piranha.core.api.WebApplicationOutputStream; import jakarta.servlet.DispatcherType; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.ServletRequest; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.MalformedURLException; import java.net.URL; import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collection; import static java.util.Collections.list; import java.util.Enumeration; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.function.Supplier; /** * The default WebApplicationResponse. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultWebApplicationResponse implements WebApplicationResponse { /** * Defines the 'ISO-8859-1' constant. */ private static final String ISO_8859_1 = "ISO-8859-1"; /** * Stores the body only flag. */ protected boolean bodyOnly; /** * Stores the buffer resetting flag. */ protected boolean bufferResetting; /** * Stores the character encoding. */ protected String characterEncoding; /** * Stores if the character encoding was set using setLocale. */ protected boolean characterEncodingLocaleSet; /** * Stores if the character encoding has been set manually. */ protected boolean characterEncodingSet; /** * Stores the committed flag. */ protected boolean committed; /** * Stores the content language */ protected String contentLanguage; /** * Stores the content length. */ protected long contentLength; /** * Stores the content type. */ protected String contentType; /** * Stores if the content type has been set manually. */ protected boolean contentTypeSet; /** * Stores the cookies. */ protected List cookies; /** * Stores if we acquired the output stream. */ protected boolean gotOutput; /** * Stores if we acquired the writer. */ protected boolean gotWriter; /** * Stores the header manager. */ protected DefaultHttpHeaderManager headerManager; /** * Stores the locale. */ protected Locale locale; /** * Stores the response closer. */ protected Runnable responseCloser; /** * Stores the status code. */ protected int status; /** * Stores the status message. */ protected String statusMessage; /** * Stores the trailer fields supplier. */ protected Supplier> trailerFields; /** * Stores the web application. */ protected WebApplication webApplication; /** * Stores the web application output stream. */ protected WebApplicationOutputStream webApplicationOutputStream; /** * Stores the writer. */ protected PrintWriter writer; /** * Constructor. */ public DefaultWebApplicationResponse() { characterEncoding = ISO_8859_1; characterEncodingLocaleSet = false; characterEncodingSet = false; committed = false; contentType = null; contentTypeSet = false; cookies = new ArrayList<>(); gotOutput = false; gotWriter = false; headerManager = new DefaultHttpHeaderManager(); locale = Locale.getDefault(); responseCloser = new Runnable() { @Override public void run() { } }; status = 200; statusMessage = null; webApplication = null; webApplicationOutputStream = new DefaultWebApplicationOutputStream(); webApplicationOutputStream.setResponse(this); writer = null; } @Override public void addCookie(Cookie cookie) { /* * Servlet:SPEC:192.4 */ verifyNotCommitted("addCookie"); this.cookies.add(cookie); } @Override public void addDateHeader(String name, long date) { addHeader(name, formatDateToGMT(date)); } @Override public void addHeader(String name, String value) { /* * REFACTOR - We need to incorporate the code in * DefaultServletRequestDispatcher here as the sendError call should * implement the functionality and NOT the dispatcher. */ if (getHeader("sendErrorCalled") != null) { return; } if (isCommitted()) { return; } /* * Servlet:SPEC:192.4 */ if (isInclude()) { return; } headerManager.addHeader(name, value); } @Override public void addIntHeader(String name, int value) { addHeader(name, Integer.toString(value)); } @Override public void closeAsyncResponse() { responseCloser.run(); } @Override public boolean containsHeader(String name) { return headerManager.containsHeader(name); } @Override public String encodeRedirectURL(String url) { String result = url; if (webApplication.getManager().getHttpSessionManager() != null) { result = webApplication.getManager().getHttpSessionManager().encodeRedirectURL(this, url); } return result; } @Override public String encodeURL(String url) { String result = url; if (webApplication.getManager().getHttpSessionManager() != null) { result = webApplication.getManager().getHttpSessionManager().encodeURL(this, url); } return result; } @Override public void flushBuffer() throws IOException { /* * Servlet:SPEC:192.3 */ if (!isCommitted()) { webApplicationOutputStream.flushBuffer(); setCommitted(true); } if (gotWriter) { writer.flush(); } } /** * Format the timestamp to a GMT string. * * @param timestamp the timestamp. * @return the GMT string. */ private String formatDateToGMT(long timestamp) { return Instant.ofEpochMilli(timestamp).atZone(ZoneId.of("GMT")) .format(DateTimeFormatter.RFC_1123_DATE_TIME); } @Override public int getBufferSize() { return webApplicationOutputStream.getBufferSize(); } @Override public String getCharacterEncoding() { String result = ISO_8859_1; if (characterEncoding != null) { result = characterEncoding; } return result; } @Override public String getContentLanguage() { return contentLanguage; } @Override public long getContentLength() { return contentLength; } @Override public String getContentType() { if (contentType == null) { return null; } String encoding = (characterEncodingLocaleSet || characterEncodingSet) ? ";charset=" + characterEncoding : ""; return contentType + encoding; } @Override public Collection getCookies() { return cookies; } @Override public String getHeader(String name) { return headerManager.getHeader(name); } @Override public Collection getHeaderNames() { List headerNames = new ArrayList<>(); Enumeration enumeration = headerManager.getHeaderNames(); if (enumeration != null) { headerNames = list(enumeration); } return headerNames; } @Override public Collection getHeaders(String name) { ArrayList result = new ArrayList<>(); Enumeration enumeration = headerManager.getHeaders(name); if (enumeration != null) { result = list(enumeration); } return result; } @Override public Locale getLocale() { return locale; } @Override public ServletOutputStream getOutputStream() throws IOException { if (!gotWriter) { gotOutput = true; return webApplicationOutputStream; } throw new IllegalStateException("Cannot get output stream as the writer was already acquired"); } @Override public Runnable getResponseCloser() { return responseCloser; } @Override public int getStatus() { return status; } @Override public String getStatusMessage() { return statusMessage; } @Override public Supplier> getTrailerFields() { return trailerFields; } @Override public WebApplication getWebApplication() { return webApplication; } @Override public WebApplicationOutputStream getWebApplicationOutputStream() { return webApplicationOutputStream; } @Override public synchronized PrintWriter getWriter() throws IOException { PrintWriter result = null; if (!gotOutput) { if (!gotWriter) { if (characterEncoding == null || !(characterEncodingSet || characterEncodingLocaleSet)) { setCharacterEncoding(ISO_8859_1); } gotWriter = true; writer = new DefaultWebApplicationPrintWriter(this, new OutputStreamWriter(webApplicationOutputStream, characterEncoding), false); } result = writer; } else { throw new IllegalStateException("Cannot get writer as the output stream was already acquired"); } return result; } @Override public boolean isBodyOnly() { return bodyOnly; } @Override public boolean isBufferResetting() { return bufferResetting; } @Override public boolean isCommitted() { return committed; } /** * Is this an include dispatch. * * @return true if it is, false otherwise. */ private boolean isInclude() { if (webApplication == null) { return false; } ServletRequest request = webApplication.getRequest(this); return request != null && request.getDispatcherType() == DispatcherType.INCLUDE; } @Override public boolean isWriterAcquired() { return gotWriter; } @Override public void reset() { verifyNotCommitted("reset"); characterEncoding = ISO_8859_1; characterEncodingSet = false; characterEncodingLocaleSet = false; committed = false; contentType = null; contentTypeSet = false; cookies = new ArrayList<>(); gotOutput = false; gotWriter = false; headerManager = new DefaultHttpHeaderManager(); locale = Locale.getDefault(); status = 200; statusMessage = null; writer = null; resetBuffer(); } @Override public void resetBuffer() { if (!committed) { bufferResetting = true; try { if (gotWriter) { /* * Flush the writer to get rid of any pending output in the * writer which will be discarded as the response is in * buffer resetting mode. */ writer.flush(); } webApplicationOutputStream.resetBuffer(); } finally { bufferResetting = false; } } else { throw new IllegalStateException("Buffer already committed, not resetting"); } } @Override public void sendError(int status) throws IOException { verifyNotCommitted("sendError"); resetBuffer(); gotWriter = false; gotOutput = false; setStatus(status); /* * REFACTOR - We need to incorporate the code in * DefaultServletRequestDispatcher here as the sendError call should * implement the functionality and NOT the dispatcher. */ setHeader("sendErrorCalled", "true"); } @Override public void sendError(int status, String statusMessage) throws IOException { verifyNotCommitted("sendError"); resetBuffer(); gotWriter = false; gotOutput = false; setStatus(status); this.statusMessage = statusMessage; setErrorMessageAttribute(); /* * REFACTOR - We need to incorporate the code in * DefaultServletRequestDispatcher here as the sendError call should * implement the functionality and NOT the dispatcher. */ setHeader("sendErrorCalled", "true"); } @Override public void sendRedirect(String location) throws IOException { sendRedirect(location, SC_FOUND, true); } @SuppressWarnings("deprecation") @Override public void sendRedirect(String location, int status, boolean resetBuffer) throws IOException { verifyNotCommitted("sendRedirect"); if (resetBuffer) { resetBuffer(); } setStatus(status); URL url; try { url = new URL(location); } catch (MalformedURLException mue) { HttpServletRequest request = (HttpServletRequest) webApplication.getRequest(this); if (location.startsWith("/")) { url = new URL(request.getScheme(), request.getServerName(), request.getServerPort(), location); } else { url = new URL(request.getScheme(), request.getServerName(), request.getServerPort(), request.getContextPath() + "/" + location); } } setHeader("Location", url.toExternalForm()); flushBuffer(); if (gotWriter) { writer.close(); } } @Override public void setBodyOnly(boolean bodyOnly) { this.bodyOnly = bodyOnly; } @Override public void setBufferSize(int bufferSize) { verifyNotCommitted("setBufferSize"); webApplicationOutputStream.setBufferSize(bufferSize); } @Override public void setCharacterEncoding(String characterEncoding) { /* * Servlet:SPEC:192.4 */ if (isInclude()) { return; } if (!gotWriter && !committed) { this.characterEncoding = characterEncoding; characterEncodingSet = true; } } @Override public void setCommitted(boolean committed) { this.committed = committed; } @Override public void setContentLength(int contentLength) { setContentLengthLong(contentLength); } @Override public void setContentLengthLong(long contentLength) { /* * Servlet:SPEC:192.4 */ if (isInclude()) { return; } this.contentLength = contentLength; headerManager.addHeader("Content-Length", String.valueOf(contentLength)); } @Override public void setContentType(String type) { /* * Servlet:SPEC:192.4 */ if (isInclude()) { return; } if (!committed) { if (type != null) { if (type.contains(";")) { contentType = type.substring(0, type.indexOf(";")).trim(); if (!gotWriter) { String encoding = type.substring(type.indexOf(";") + 1).trim(); if (encoding.contains("=")) { encoding = encoding.substring(encoding.indexOf("=") + 1).trim(); setCharacterEncoding(encoding); } } } else { contentType = type; } } else { contentType = type; } contentTypeSet = true; } } @Override public void setDateHeader(String name, long date) { setHeader(name, formatDateToGMT(date)); } /** * Set the error message attribute on the request. */ private void setErrorMessageAttribute() { if (webApplication != null) { ServletRequest request = webApplication.getRequest(this); if (request != null) { request.setAttribute(RequestDispatcher.ERROR_MESSAGE, statusMessage); } } } @Override public void setHeader(String name, String value) { /* * REFACTOR - We need to incorporate the code in * DefaultServletRequestDispatcher here as the sendError call should * implement the functionality and NOT the dispatcher. */ if (getHeader("sendErrorCalled") != null) { return; } /* * Servlet:SPEC:192.4 */ if (isInclude()) { return; } headerManager.setHeader(name, value); } @Override public void setIntHeader(String name, int value) { setHeader(name, Integer.toString(value)); } @Override public void setLocale(Locale locale) { if (!committed) { this.locale = locale; this.contentLanguage = locale.toLanguageTag(); if (webApplication == null) { return; } if (!gotWriter && !characterEncodingSet) { LocaleEncodingManager localeEncodingManager = webApplication.getManager().getLocaleEncodingManager(); if (localeEncodingManager != null) { String encoding = localeEncodingManager.getCharacterEncoding(locale.toString()); if (encoding == null) { encoding = localeEncodingManager.getCharacterEncoding(locale.getLanguage()); } if (encoding != null && !characterEncodingSet) { characterEncoding = encoding; characterEncodingLocaleSet = true; } } } } } @Override public void setResponseCloser(Runnable responseCloser) { this.responseCloser = responseCloser; } @Override public void setStatus(int status) { /* * Servlet:SPEC:192.4 */ if (isInclude()) { return; } if (!isCommitted()) { this.status = status; } } @Override public void setTrailerFields(Supplier> trailerFields) { if (isCommitted()) { throw new IllegalStateException("Response is already committed"); } HttpServletRequest httpServletRequest = (HttpServletRequest) webApplication.getRequest(this); if (httpServletRequest == null) { throw new IllegalStateException("Not supported on this type of ServletRequest"); } if (httpServletRequest.getProtocol().equalsIgnoreCase("HTTP/1.0")) { throw new IllegalStateException("Not supported with HTTP/1.0 protocol"); } this.trailerFields = trailerFields; } @Override public void setWebApplication(WebApplication webApplication) { this.webApplication = webApplication; } @Override public void setWebApplicationOutputStream(WebApplicationOutputStream outputStream) { this.webApplicationOutputStream = outputStream; } /** * Verify we are not committed. * * @param methodName the method we are checking for. */ protected void verifyNotCommitted(String methodName) { if (isCommitted()) { throw new IllegalStateException("Response already committed in " + methodName); } } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplicationResponseBuilder.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplication; import java.io.OutputStream; /** * The DefaultWebApplicationResponseBuilder. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultWebApplicationResponseBuilder { /** * Stores the body only flag. */ private boolean bodyOnly = false; /** * Stores the underlying output stream. */ private OutputStream underlyingOutputStream; /** * Stores the web application. */ private WebApplication webApplication; /** * Constructor. */ public DefaultWebApplicationResponseBuilder() { } /** * Set the body only flag. * * @param bodyOnly the body only flag. * @return the builder. */ public DefaultWebApplicationResponseBuilder bodyOnly(boolean bodyOnly) { this.bodyOnly = bodyOnly; return this; } /** * Build the response. * * @return response. */ public DefaultWebApplicationResponse build() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setBodyOnly(bodyOnly); if (underlyingOutputStream != null) { response.getWebApplicationOutputStream().setOutputStream(underlyingOutputStream); } if (webApplication != null) { response.setWebApplication(webApplication); } return response; } /** * Set the underlying output stream. * * @param underlyingOutputStream the underlying output stream. * @return the builder. */ public DefaultWebApplicationResponseBuilder underlyingOutputStream(OutputStream underlyingOutputStream) { this.underlyingOutputStream = underlyingOutputStream; return this; } /** * Set the web application. * * @param webApplication the web application. * @return the builder. */ public DefaultWebApplicationResponseBuilder webApplication(WebApplication webApplication) { this.webApplication = webApplication; return this; } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebConnection.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplicationRequest; import cloud.piranha.core.api.WebApplicationResponse; import jakarta.servlet.ServletInputStream; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.WebConnection; import java.io.IOException; /** * The default WebConnection. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultWebConnection implements WebConnection { /** * Stores the request. */ private final WebApplicationRequest request; /** * Stores the response. */ private final WebApplicationResponse response; /** * Constructor. * * @param request the request. * @param response the response. */ public DefaultWebConnection( WebApplicationRequest request, WebApplicationResponse response) { this.request = request; this.response = response; } @Override public ServletInputStream getInputStream() throws IOException { return request.getInputStream(); } @Override public ServletOutputStream getOutputStream() throws IOException { return response.getOutputStream(); } @Override public void close() throws Exception { request.getInputStream().close(); response.getOutputStream().close(); } } ================================================ FILE: core/impl/src/main/java/cloud/piranha/core/impl/WarFileExtractor.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import static java.lang.System.Logger.Level.WARNING; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; /** * A utility class to extract WAR files onto the file system. * * @author Manfred Riem (mriem@manorrock.com) */ public class WarFileExtractor { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(WarFileExtractor.class.getName()); /** * Constructor. */ private WarFileExtractor() { } /** * Extract the WAR file to the given directory. * * @param warFile the WAR file. * @param directory the web application directory. */ public static void extractWarFile(File warFile, File directory) { if (!directory.exists()) { directory.mkdirs(); } try (ZipInputStream zipInput = new ZipInputStream(new FileInputStream(warFile))) { ZipEntry entry = zipInput.getNextEntry(); while (entry != null) { String filePath = directory + File.separator + entry.getName(); if (!entry.isDirectory()) { File file = new File(filePath); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } extractZipInputStream(zipInput, filePath); } zipInput.closeEntry(); entry = zipInput.getNextEntry(); } } catch (IOException ioe) { LOGGER.log(WARNING, "I/O error occurred while extracting WAR file", ioe); } } /** * Extract the zip input stream. * * @param zipInput the zip input stream. * @param filePath the file path. * @throws IOException when an I/O error occurs. */ private static void extractZipInputStream(ZipInputStream zipInput, String filePath) throws IOException { try (BufferedOutputStream bufferOutput = new BufferedOutputStream(new FileOutputStream(filePath))) { byte[] bytesIn = new byte[8192]; int read; while ((read = zipInput.read(bytesIn)) != -1) { bufferOutput.write(bytesIn, 0, read); } } } } ================================================ FILE: core/impl/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the default implementations for the Piranha APIs. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.core.impl { exports cloud.piranha.core.impl; opens cloud.piranha.core.impl; requires transitive cloud.piranha.core.api; requires transitive cloud.piranha.resource.impl; } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/AsyncContextTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationRequest; import cloud.piranha.core.api.WebApplicationResponse; import jakarta.servlet.AsyncContext; import jakarta.servlet.AsyncEvent; import jakarta.servlet.AsyncListener; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRegistration; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for testing everything related to the AsyncContext API. * * @author Manfred Riem (mriem@manorrock.com) */ class AsyncContextTest { private WebApplication createWebApplication() { return new DefaultWebApplication(); } private WebApplicationRequest createWebApplicationRequest() { return new DefaultWebApplicationRequest(); } private WebApplicationResponse createWebApplicationResponse() { return new DefaultWebApplicationResponse(); } /** * Test addListener method. */ @Test void testAddListener() { WebApplication webApplication = createWebApplication(); WebApplicationRequest request = createWebApplicationRequest(); request.setWebApplication(webApplication); request.setAsyncSupported(true); WebApplicationResponse response = createWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); AsyncContext context = request.startAsync(); context.addListener(new AsyncListener() { @Override public void onComplete(AsyncEvent event) throws IOException { event.getSuppliedRequest().getServletContext().setAttribute("onComplete", "true"); } @Override public void onTimeout(AsyncEvent event) throws IOException { } @Override public void onError(AsyncEvent event) throws IOException { } @Override public void onStartAsync(AsyncEvent event) throws IOException { } }); context.complete(); assertNotNull(webApplication.getAttribute("onComplete")); } /** * Test complete method. */ @Test void testComplete() { WebApplication webApplication = createWebApplication(); WebApplicationRequest request = createWebApplicationRequest(); request.setWebApplication(webApplication); request.setAsyncSupported(true); WebApplicationResponse response = createWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); AsyncContext context = request.startAsync(); context.complete(); assertTrue(response.isCommitted()); } /** * Test createListener method. * * @throws ServletException when a serious error occurs. */ @Test void testCreateListener() throws ServletException { WebApplication webApplication = createWebApplication(); WebApplicationRequest request = createWebApplicationRequest(); request.setWebApplication(webApplication); request.setAsyncSupported(true); WebApplicationResponse response = createWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); AsyncContext context = request.startAsync(); assertNotNull(context.createListener(TestCreateListenerListener.class)); } /** * Test createListener method. * * @throws ServletException when a serious error occurs. */ @Test void testCreateListener2() throws ServletException { WebApplication webApplication = createWebApplication(); WebApplicationRequest request = createWebApplicationRequest(); request.setWebApplication(webApplication); request.setAsyncSupported(true); WebApplicationResponse response = createWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); AsyncContext context = request.startAsync(); assertThrows(ServletException.class, () -> { context.createListener(TestCreateListener2Listener.class); }); } /** * Test dispatch method. */ @Test void testDispatch() { WebApplication webApplication = createWebApplication(); ServletRegistration.Dynamic registration = webApplication.addServlet("TestDispatchServlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { AsyncContext context = request.startAsync(); context.dispatch("/testDispatchB"); context.dispatch("/testDispatchC"); } }); registration.setAsyncSupported(true); webApplication.addServletMapping("TestDispatchServlet", "/testDispatch"); webApplication.initialize(); webApplication.start(); WebApplicationRequest request = createWebApplicationRequest(); request.setServletPath("/testDispatch"); request.setWebApplication(webApplication); request.setAsyncSupported(true); WebApplicationResponse response = createWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); assertThrows(IllegalStateException.class, () -> webApplication.service(request, response)); } /** * Test getRequest method. */ @Test void testGetRequest() { WebApplication webApplication = createWebApplication(); WebApplicationRequest request = createWebApplicationRequest(); request.setWebApplication(webApplication); request.setAsyncSupported(true); WebApplicationResponse response = createWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); AsyncContext context = request.startAsync(); assertNotNull(context.getRequest()); } /** * Test getResponse method. */ @Test void testGetResponse() { WebApplication webApplication = createWebApplication(); WebApplicationRequest request = createWebApplicationRequest(); request.setWebApplication(webApplication); request.setAsyncSupported(true); WebApplicationResponse response = createWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); AsyncContext context = request.startAsync(); assertNotNull(context.getResponse()); } /** * Test getTimeout method. */ @Test void testGetTimeout() { WebApplication webApplication = createWebApplication(); WebApplicationRequest request = createWebApplicationRequest(); request.setWebApplication(webApplication); request.setAsyncSupported(true); WebApplicationResponse response = createWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); AsyncContext context = request.startAsync(); assertEquals(30000, context.getTimeout()); } /** * Test hasOriginalRequestAndResponse method. */ @Test void testHasOriginalRequestAndResponse() { WebApplication webApplication = createWebApplication(); WebApplicationRequest request = createWebApplicationRequest(); request.setWebApplication(webApplication); request.setAsyncSupported(true); WebApplicationResponse response = createWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); AsyncContext context = request.startAsync(); assertTrue(context.hasOriginalRequestAndResponse()); assertEquals(request, context.getRequest()); assertEquals(response, context.getResponse()); } /** * Test setTimeout method. */ @Test void testSetTimeout() { WebApplication webApplication = createWebApplication(); WebApplicationRequest request = createWebApplicationRequest(); request.setWebApplication(webApplication); request.setAsyncSupported(true); WebApplicationResponse response = createWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); AsyncContext context = request.startAsync(); context.setTimeout(60000); assertEquals(60000, context.getTimeout()); } /** * Listener that is used by testCreateListener. */ public static class TestCreateListenerListener implements AsyncListener { /** * Default constructor. */ public TestCreateListenerListener() { } @Override public void onComplete(AsyncEvent event) throws IOException { } @Override public void onTimeout(AsyncEvent event) throws IOException { } @Override public void onError(AsyncEvent event) throws IOException { } @Override public void onStartAsync(AsyncEvent event) throws IOException { } } /** * Listener that is used by testCreateListener2. */ public static class TestCreateListener2Listener implements AsyncListener { /** * Constructor. * * @throws ServletException when constructor is called. */ public TestCreateListener2Listener() throws ServletException { throw new ServletException(); } @Override public void onComplete(AsyncEvent event) throws IOException { } @Override public void onTimeout(AsyncEvent event) throws IOException { } @Override public void onError(AsyncEvent event) throws IOException { } @Override public void onStartAsync(AsyncEvent event) throws IOException { } } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/CookieParserTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import jakarta.servlet.http.Cookie; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; class CookieParserTest { /* TODO - REVIEW FOR SERVLET 6 @Nested class RFC2109 { @Test void parseSingleCookie() { Cookie[] cookies = CookieParser.parse("$Version=\"1\"; Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\""); assertNotNull(cookies); assertEquals(1, cookies.length); Cookie cookie = cookies[0]; assertEquals(1, cookie.getVersion()); assertEquals("Customer", cookie.getName()); assertEquals("WILE_E_COYOTE", cookie.getValue()); assertEquals("/acme", cookie.getPath()); } @Test void parseMultipleCookies() { Cookie[] cookies = CookieParser.parse("$Version=\"1\";Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\";\nPart_Number=\"Rocket_Launcher_0001\"; $Path=\"/ammo\""); assertNotNull(cookies); assertEquals(2, cookies.length); Cookie cookie1 = cookies[0]; assertEquals(1, cookie1.getVersion()); assertEquals("Customer", cookie1.getName()); assertEquals("WILE_E_COYOTE", cookie1.getValue()); assertEquals("/acme", cookie1.getPath()); Cookie cookie2 = cookies[1]; assertEquals(1, cookie2.getVersion()); assertEquals("Part_Number", cookie2.getName()); assertEquals("Rocket_Launcher_0001", cookie2.getValue()); assertEquals("/ammo", cookie2.getPath()); } } */ @Nested @SuppressWarnings({"removal"}) class Netscape { @Test void parseSingleCookie() { Cookie[] cookies = CookieParser.parse("CUSTOMER=WILE_E_COYOTE"); assertNotNull(cookies); assertEquals(1, cookies.length); Cookie cookie = cookies[0]; assertEquals(0, cookie.getVersion()); assertEquals("CUSTOMER", cookie.getName()); assertEquals("WILE_E_COYOTE", cookie.getValue()); } @Test void parseMultipleCookies() { Cookie[] cookies = CookieParser.parse("CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001"); assertNotNull(cookies); assertEquals(2, cookies.length); Cookie cookie1 = cookies[0]; assertEquals(0, cookie1.getVersion()); assertEquals("CUSTOMER", cookie1.getName()); assertEquals("WILE_E_COYOTE", cookie1.getValue()); Cookie cookie2 = cookies[1]; assertEquals(0, cookie2.getVersion()); assertEquals("PART_NUMBER", cookie2.getName()); assertEquals("ROCKET_LAUNCHER_0001", cookie2.getValue()); } } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/DefaultAnnotationManagerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import java.lang.annotation.Annotation; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; /** * The JUnit tests for the DefaultAnnotationManager class. * * @author Manfred Riem (mriem@manorrock.com) */ class DefaultAnnotationManagerTest { /** * Test addAnnotatedClass method. */ @Test void testAddAnnotatedClass() { try { DefaultAnnotationManager manager = new DefaultAnnotationManager(); manager.addAnnotatedClass(null, null); fail(); } catch(IllegalArgumentException iae) { // expecting this so swallowing it up } } /** * Test addAnnotatedClass method. */ @Test void testAddAnnotatedClass2() { DefaultAnnotationManager manager = new DefaultAnnotationManager(); Annotation annotation = new Annotation() { @Override public Class annotationType() { throw new UnsupportedOperationException("Not supported yet."); } }; manager.addAnnotatedClass(annotation.getClass(), Object.class); manager.addAnnotatedClass(annotation.getClass(), String.class); assertEquals(2, manager.getAnnotatedClass(annotation.getClass()).size()); } /** * Test addAnnotation method. */ @Test void testAddAnnotation() { try { DefaultAnnotationManager manager = new DefaultAnnotationManager(); manager.addAnnotation(null); fail(); } catch(IllegalArgumentException iae) { // expecting this so swallowing it up } } /** * Test addInstance method. */ @Test void testAddInstance() { try { DefaultAnnotationManager manager = new DefaultAnnotationManager(); manager.addInstance(null, null); fail(); } catch(IllegalArgumentException iae) { // expecting this so swallowing it up } } /** * Test getAnnotations method. */ @Test void testGetAnnotations() { DefaultAnnotationManager manager = new DefaultAnnotationManager(); assertNotNull(manager.getAnnotations(Object.class)); } /** * Test getAnnotations method. */ @Test void testGetAnnotations2() { DefaultAnnotationManager manager = new DefaultAnnotationManager(); assertNotNull(manager.getAnnotations(new Class[0])); } /** * Test getInstances method. */ @Test void testGetInstances() { DefaultAnnotationManager manager = new DefaultAnnotationManager(); assertNotNull(manager.getInstances(Object.class)); } /** * Test getInstances method. */ @Test void testGetInstances2() { DefaultAnnotationManager manager = new DefaultAnnotationManager(); assertNotNull(manager.getInstances(new Class[0])); } /** * Test getAnnotationsByTarget method. */ @Test void testGetAnnotationsByTarget() { DefaultAnnotationManager manager = new DefaultAnnotationManager(); assertNotNull(manager.getAnnotationsByTarget(null, null)); } /** * Test getAnnotatedClass method. */ @Test void testGetAnnotatedClass() { DefaultAnnotationManager manager = new DefaultAnnotationManager(); assertNotNull(manager.getAnnotatedClass(null)); } /** * Test getAnnotatedClasses method. */ @Test void testGetAnnotatedClasses() { DefaultAnnotationManager manager = new DefaultAnnotationManager(); assertNotNull(manager.getAnnotatedClasses(null)); } /** * Test getAnnotatedClasses method. */ @Test void testGetAnnotatedClasses2() { DefaultAnnotationManager manager = new DefaultAnnotationManager(); Annotation annotation = new Annotation() { @Override public Class annotationType() { throw new UnsupportedOperationException("Not supported yet."); } }; manager.addAnnotatedClass(annotation.getClass(), Object.class); manager.addAnnotatedClass(annotation.getClass(), String.class); assertEquals(2, manager.getAnnotatedClasses(new Class[] {annotation.getClass()}).size()); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/DefaultErrorPageManagerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import org.junit.jupiter.api.Test; import jakarta.servlet.ServletException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class DefaultErrorPageManagerTest { @Test void testPagesByCode() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultErrorPageManager errorPageManager = new DefaultErrorPageManager(); errorPageManager.addErrorPage(501, "/501"); errorPageManager.addErrorPage(404, "/404"); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setStatus(501); assertEquals("/501", errorPageManager.getErrorPage(null, response)); response.setStatus(404); assertEquals("/404", errorPageManager.getErrorPage(null, response)); } @Test void testPagesByCode2() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultErrorPageManager errorPageManager = new DefaultErrorPageManager(); errorPageManager.addErrorPage(404, "/404"); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setStatus(501); assertNull(errorPageManager.getErrorPage(null, response)); } @Test void testPagesByCode3() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultErrorPageManager errorPageManager = new DefaultErrorPageManager(); errorPageManager.addErrorPage(404, "/404"); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setStatus(500); assertNull(errorPageManager.getErrorPage(new NullPointerException(), response)); } @Test void testPagesByException() { DefaultErrorPageManager errorPageManager = new DefaultErrorPageManager(); errorPageManager.addErrorPage(IllegalArgumentException.class.getName(), "/IAE"); assertEquals("/IAE", errorPageManager.getErrorPage(new IllegalArgumentException(), null)); } @Test void testPagesByException2() { DefaultErrorPageManager errorPageManager = new DefaultErrorPageManager(); errorPageManager.addErrorPage(IndexOutOfBoundsException.class.getName(), "/IOUB"); assertEquals("/IOUB", errorPageManager.getErrorPage(new ArrayIndexOutOfBoundsException(), null)); } @Test void testPagesByException3() { DefaultErrorPageManager errorPageManager = new DefaultErrorPageManager(); errorPageManager.addErrorPage(IllegalArgumentException.class.getName(), "/IAE"); assertEquals("/IAE", errorPageManager.getErrorPage(new ServletException(new IllegalArgumentException()), null)); } @Test void testPagesByException4() { DefaultErrorPageManager errorPageManager = new DefaultErrorPageManager(); errorPageManager.addErrorPage(IllegalArgumentException.class.getName(), "/IAE"); assertNull(errorPageManager.getErrorPage(new IndexOutOfBoundsException(), null)); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/DefaultFilterEnvironmentTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.FilterEnvironment; import cloud.piranha.core.api.WebApplication; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.FilterRegistration; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import java.io.IOException; import java.util.HashMap; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the DefaultFilterEnvironment class. * * @author Manfred Riem (mriem@manorrock.com) */ class DefaultFilterEnvironmentTest { /** * Create a filter that just calls the next in the chain. * * @return the filter. */ private Filter createNoopFilter() { return new Filter() { @Override public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request, response); } }; } /** * Create the web application. * * @return the web application. */ private WebApplication createWebApplication() { return new DefaultWebApplication(); } /** * Test getName method. */ @Test void testGetName() { WebApplication webApplication = createWebApplication(); webApplication.addFilter("testGetNameFilter", createNoopFilter()); FilterRegistration registration = webApplication.getFilterRegistration("testGetNameFilter"); assertTrue(registration instanceof DefaultFilterEnvironment); assertEquals("testGetNameFilter", registration.getName()); } /** * Test getClassName method. */ @Test void testGetClassName() { WebApplication webApplication = createWebApplication(); webApplication.addFilter("testGetClassNameFilter", TestGetClassNameFilter.class); FilterRegistration registration = webApplication.getFilterRegistration("testGetClassNameFilter"); assertTrue(registration instanceof DefaultFilterEnvironment); assertEquals(TestGetClassNameFilter.class.getName(), registration.getClassName()); } /** * Test getInitParameterNames method. */ @Test void testGetInitParameterNames() { WebApplication webApplication = createWebApplication(); webApplication.addFilter("testGetInitParametersFilter", createNoopFilter()); FilterRegistration registration = webApplication.getFilterRegistration("testGetInitParametersFilter"); registration.setInitParameter("key", "value"); assertTrue(registration instanceof DefaultFilterEnvironment); FilterEnvironment environment = (FilterEnvironment) registration; assertNotNull(environment.getInitParameterNames()); assertEquals("key", environment.getInitParameterNames().nextElement()); } /** * Test getInitParameters method. */ @Test void testGetInitParameters() { WebApplication webApplication = createWebApplication(); webApplication.addFilter("testGetInitParametersFilter", createNoopFilter()); FilterRegistration registration = webApplication.getFilterRegistration("testGetInitParametersFilter"); assertTrue(registration instanceof DefaultFilterEnvironment); assertNotNull(registration.getInitParameters()); } /** * Test getUrlPatternMappings method. */ @Test void testGetUrlPatternMappings() { WebApplication webApplication = createWebApplication(); webApplication.addFilter("testGetUrlPatternMappingsFilter", createNoopFilter()); FilterRegistration registration = webApplication.getFilterRegistration("testGetUrlPatternMappingsFilter"); assertTrue(registration instanceof DefaultFilterEnvironment); assertTrue(registration.getUrlPatternMappings().isEmpty()); } /** * Test setInitParameters method. */ @Test void testSetInitParameters() { WebApplication webApplication = createWebApplication(); webApplication.addFilter("testSetInitParametersFilter", createNoopFilter()); FilterRegistration registration = webApplication.getFilterRegistration("testSetInitParametersFilter"); registration.setInitParameter("name", "value"); assertTrue(registration instanceof DefaultFilterEnvironment); assertTrue(registration.setInitParameters(new HashMap<>()).isEmpty()); } /** * Test setInitParameters method. */ @Test void testSetInitParameters2() { WebApplication webApplication = createWebApplication(); webApplication.addFilter("testSetInitParameters2Filter", createNoopFilter()); FilterRegistration registration = webApplication.getFilterRegistration("testSetInitParameters2Filter"); HashMap parameters = new HashMap<>(); parameters.put(null, null); assertTrue(registration instanceof DefaultFilterEnvironment); assertThrows(IllegalArgumentException.class, () -> registration.setInitParameters(parameters)); } /** * Test setInitParameters method. */ @Test void testSetInitParameters3() { WebApplication webApplication = createWebApplication(); webApplication.addFilter("testSetInitParameters3Filter", createNoopFilter()); FilterRegistration registration = webApplication.getFilterRegistration("testSetInitParameters3Filter"); HashMap parameters = new HashMap<>(); parameters.put("name", null); assertTrue(registration instanceof DefaultFilterEnvironment); assertThrows(IllegalArgumentException.class, () -> registration.setInitParameters(parameters)); } /** * Test setInitParameters method. */ @Test void testSetInitParameters4() { WebApplication webApplication = createWebApplication(); webApplication.addFilter("testSetInitParameters4Filter", createNoopFilter()); FilterRegistration registration = webApplication.getFilterRegistration("testSetInitParameters4Filter"); HashMap parameters = new HashMap<>(); parameters.put("name", "value"); assertTrue(registration instanceof DefaultFilterEnvironment); assertTrue(registration.setInitParameters(parameters).isEmpty()); } /** * Test setInitParameters method. */ @Test void testSetInitParameters5() { WebApplication webApplication = createWebApplication(); webApplication.addFilter("testSetInitParameters5Filter", createNoopFilter()); FilterRegistration registration = webApplication.getFilterRegistration("testSetInitParameters5Filter"); HashMap parameters = new HashMap<>(); parameters.put("name", "value"); assertTrue(registration instanceof DefaultFilterEnvironment); assertTrue(registration.setInitParameters(parameters).isEmpty()); assertFalse(registration.setInitParameters(parameters).isEmpty()); } /** * Test filter for getClassName method. */ public class TestGetClassNameFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { } } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/DefaultHttpHeaderManagerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Enumeration; import java.util.Locale; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; /** * @author Manfred Riem (mriem@manorrock.com) */ class DefaultHttpHeaderManagerTest { /** * Test addHeader method. */ @Test void testAddHeader() { DefaultHttpHeaderManager manager = new DefaultHttpHeaderManager(); manager.addHeader("NAME", "VALUE"); assertEquals("VALUE", manager.getHeader("NAME")); } /** * Test addHeader method. */ @Test void testAddHeader2() { DefaultHttpHeaderManager manager = new DefaultHttpHeaderManager(); manager.addHeader("NAME", "VALUE"); manager.addHeader("NAME", "VALUE2"); assertEquals("VALUE", manager.getHeader("NAME")); Enumeration values = manager.getHeaders("NAME"); assertTrue(values.hasMoreElements()); assertNotNull(values.nextElement()); assertTrue(values.hasMoreElements()); assertNotNull(values.nextElement()); assertFalse(values.hasMoreElements()); } /** * Test containsHeader method. */ @Test void testContainsHeader() { DefaultHttpHeaderManager manager = new DefaultHttpHeaderManager(); manager.addHeader("NAME", "VALUE"); assertEquals("VALUE", manager.getHeader("NAME")); assertTrue(manager.containsHeader("NAME")); } /** * Test getDateHeader method. */ @Test void testGetDateHeader() { DefaultHttpHeaderManager manager = new DefaultHttpHeaderManager(); SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US); manager.addHeader("NAME", format.format(new Date())); assertTrue(manager.getDateHeader("NAME") > 0); } /** * Test getDateHeader method. */ @Test void testGetDateHeader2() { DefaultHttpHeaderManager manager = new DefaultHttpHeaderManager(); assertEquals(-1, manager.getDateHeader("NAME")); } /** * Test getDateHeader method. */ @Test void testGetDateHeader3() { DefaultHttpHeaderManager manager = new DefaultHttpHeaderManager(); manager.addHeader("NAME", "KABOOM"); assertThrows(IllegalArgumentException.class, () -> manager.getDateHeader("NAME")); } /** * Test getHeader method. */ @Test void testGetHeader() { DefaultHttpHeaderManager manager = new DefaultHttpHeaderManager(); assertNull(manager.getHeader("NAME")); } /** * Test getDateHeader method. */ @Test void testGetHeaderNames() { DefaultHttpHeaderManager manager = new DefaultHttpHeaderManager(); manager.addHeader("NAME", "VALUE"); Enumeration names = manager.getHeaderNames(); assertTrue(names.hasMoreElements()); assertEquals("NAME", names.nextElement()); } /** * Test getHeaders method. */ @Test void testGetHeaders() { DefaultHttpHeaderManager manager = new DefaultHttpHeaderManager(); assertNotNull(manager.getHeaders("NAME")); assertFalse(manager.getHeaders("NAME").hasMoreElements()); } /** * Test getIntHeader method. */ @Test void testGetIntHeader() { DefaultHttpHeaderManager manager = new DefaultHttpHeaderManager(); manager.addHeader("NAME", "1"); assertEquals(1, manager.getIntHeader("NAME")); } /** * Test getIntHeader method. */ @Test void testGetIntHeader2() { DefaultHttpHeaderManager manager = new DefaultHttpHeaderManager(); manager.setHeader("NAME", "abcd"); assertThrows(IllegalArgumentException.class, () -> manager.getIntHeader("NAME")); } /** * Test getIntHeader method. */ @Test void testGetIntHeader3() { DefaultHttpHeaderManager manager = new DefaultHttpHeaderManager(); assertEquals(-1, manager.getIntHeader("NAME")); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/DefaultPiranhaConfigurationTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import java.io.File; import static java.lang.Boolean.TRUE; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * The JUnit tests for the DefaultPiranhaConfiguration class. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultPiranhaConfigurationTest { /** * Test getBoolean method. */ @Test public void testGetBoolean() { String key = "mykey"; DefaultPiranhaConfiguration configuration = new DefaultPiranhaConfiguration(); assertFalse(configuration.getBoolean(key, false)); configuration.setBoolean(key, TRUE); assertTrue(configuration.getBoolean(key, false)); } /** * Test getClass method. */ @Test public void testGetClass() { String key = "mykey"; DefaultPiranhaConfiguration configuration = new DefaultPiranhaConfiguration(); assertNull(configuration.getClass(key)); configuration.setClass(key, Object.class); assertNotNull(configuration.getClass(key)); } /** * Test getFile method. */ @Test public void testGetFile() { String key = "mykey"; DefaultPiranhaConfiguration configuration = new DefaultPiranhaConfiguration(); assertNull(configuration.getFile(key)); configuration.setFile(key, new File(".")); assertNotNull(configuration.getFile(key)); } /** * Test getInteger method. */ @Test public void testGetInteger() { String key = "mykey"; DefaultPiranhaConfiguration configuration = new DefaultPiranhaConfiguration(); assertNull(configuration.getInteger(key)); configuration.setInteger(key, Integer.MAX_VALUE); assertNotNull(configuration.getInteger(key)); } /** * Test getLong method. */ @Test public void testGetLong() { String key = "mykey"; DefaultPiranhaConfiguration configuration = new DefaultPiranhaConfiguration(); assertNull(configuration.getLong(key)); configuration.setLong(key, Long.MAX_VALUE); assertNotNull(configuration.getLong(key)); } /** * Test getString method. */ @Test public void testGetString() { String key = "mykey"; DefaultPiranhaConfiguration configuration = new DefaultPiranhaConfiguration(); assertNull(configuration.getString(key)); configuration.setString(key, "mystring"); assertNotNull(configuration.getString(key)); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/DefaultPushBuilderTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the DefaultPushBuilder class. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultPushBuilderTest { /** * Test addHeader method. */ @Test void testAddHeader() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); DefaultPushBuilder builder = new DefaultPushBuilder(request); builder.addHeader("name", "value"); assertEquals("value", builder.getHeader("name")); builder.removeHeader("name"); assertNull(builder.getHeader("name")); } /** * Test getHeaderNames method. */ @Test void testGetHeaderNames() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); DefaultPushBuilder builder = new DefaultPushBuilder(request); builder.addHeader("name", "value"); Set names = builder.getHeaderNames(); assertFalse(names.isEmpty()); assertTrue(names.contains("name")); } /** * Test method method. */ @Test void testMethod() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); DefaultPushBuilder builder = new DefaultPushBuilder(request); builder.method("GET"); assertEquals("GET", builder.getMethod()); } /** * Test path method. */ @Test void testPath() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); DefaultPushBuilder builder = new DefaultPushBuilder(request); builder.path("index.html"); assertEquals("index.html", builder.getPath()); } /** * Test push method. */ @Test void testPush() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); DefaultPushBuilder builder = new DefaultPushBuilder(request); builder.path("myimage.png"); assertEquals("myimage.png", builder.getPath()); builder.push(); assertNotEquals("myimage.png", builder.getPath()); } /** * Test queryString method. */ @Test void testQueryString() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); DefaultPushBuilder builder = new DefaultPushBuilder(request); builder.queryString("foo=bar"); assertEquals("foo=bar", builder.getQueryString()); } /** * Test sessionId method. */ @Test void testSessionId() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); DefaultPushBuilder builder = new DefaultPushBuilder(request); builder.sessionId("mysessionid"); assertEquals("mysessionid", builder.getSessionId()); } /** * Test setHeader method. */ @Test void testSetHeader() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); DefaultPushBuilder builder = new DefaultPushBuilder(request); builder.setHeader("name", "value"); assertEquals("value", builder.getHeader("name")); builder.removeHeader("name"); assertNull(builder.getHeader("name")); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/DefaultSecurityManagerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; /** * The JUnit tests for the DefaultSecurityManager class. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultSecurityManagerTest { /** * Test getSecurityConstraints method. */ @Test public void testGetSecurityConstraints() { DefaultSecurityManager manager = new DefaultSecurityManager(); assertNotNull(manager.getSecurityConstraints()); manager.setSecurityConstraints(null); assertNull(manager.getSecurityConstraints()); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/DefaultServletEnvironmentTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import jakarta.servlet.MultipartConfigElement; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the DefaultServletEnvironment class. * * @author Manfred Riem (mriem@manorrock.com). */ class DefaultServletEnvironmentTest { /** * Test getLoadOnStartup method. */ @Test void testGetLoadOnStartup() { TestSnoopServlet servlet = new TestSnoopServlet(); DefaultServletEnvironment environment = new DefaultServletEnvironment(null, null, servlet); environment.setLoadOnStartup(1); assertEquals(1, environment.getLoadOnStartup()); } /** * Test setAsyncSupported method. */ @Test void testSetAsyncSupported() { TestSnoopServlet servlet = new TestSnoopServlet(); DefaultServletEnvironment environment = new DefaultServletEnvironment(null, null, servlet); assertFalse(environment.isAsyncSupported()); environment.setAsyncSupported(true); assertTrue(environment.isAsyncSupported()); } /** * Test setMultipartConfig method. */ @Test void testMultipartConfig() { TestSnoopServlet servlet = new TestSnoopServlet(); DefaultServletEnvironment environment = new DefaultServletEnvironment(null, null, servlet); assertNull(environment.getMultipartConfig()); environment.setMultipartConfig(new MultipartConfigElement("/location")); assertNotNull(environment.getMultipartConfig()); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/DefaultWebApplicationBuilderTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplication; import cloud.piranha.resource.api.ResourceManager; import cloud.piranha.resource.impl.DirectoryResource; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.http.HttpServlet; import java.io.IOException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * The JUnit tests for the DefaultWebApplicationBuilder class. * * @author Manfred Riem (mriem@manorrock.com) */ class DefaultWebApplicationBuilderTest { /** * Test build method. */ @Test void testBuild() { assertNotNull(new DefaultWebApplicationBuilder().build()); } /** * Test directoryResource method. */ @Test void testDirectoryResource() { WebApplication webApplication = new DefaultWebApplicationBuilder() .directoryResource("target") .build(); ResourceManager manager = webApplication.getManager().getResourceManager(); assertTrue(manager.getResourceList().get(0) instanceof DirectoryResource); } /** * Test filter method. */ @Test void testFilter() { WebApplication webApplication = new DefaultWebApplicationBuilder() .filter("testFilter", "TestFilter") .build(); assertEquals("TestFilter", webApplication.getFilterRegistration("testFilter").getClassName()); } /** * Test filter method. */ @Test void testFilter2() { WebApplication webApplication = new DefaultWebApplicationBuilder() .filter("testFilter", TestFilter.class) .build(); assertEquals(TestFilter.class.getName(), webApplication.getFilterRegistration("testFilter").getClassName()); } /** * Test filter method. */ @Test void testFilter3() { WebApplication webApplication = new DefaultWebApplicationBuilder() .filter("testFilter", new TestFilter()) .build(); assertEquals(TestFilter.class.getName(), webApplication.getFilterRegistration("testFilter").getClassName()); } /** * Test filterInitParam method. */ @Test void testFilterInitParam() { WebApplication webApplication = new DefaultWebApplicationBuilder() .filter("testFilter", TestFilter.class) .filterInitParam("testFilter", "name", "value") .build(); assertEquals("value", webApplication.getFilterRegistration("testFilter").getInitParameter("name")); } /** * Test initParam method. */ @Test void testInitParam() { WebApplication webApplication = new DefaultWebApplicationBuilder() .initParam("name", "value") .build(); assertEquals("value", webApplication.getInitParameter("name")); } /** * Test servlet method. */ @Test void testServlet() { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("testServlet", "TestServlet") .build(); assertEquals("TestServlet", webApplication.getServletRegistration("testServlet").getClassName()); } /** * Test servlet method. */ @Test void testServlet2() { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("testServlet", TestServlet.class) .build(); assertEquals(TestServlet.class.getName(), webApplication.getServletRegistration("testServlet").getClassName()); } /** * Test servlet method. */ @Test void testServlet3() { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("testServlet", new TestServlet()) .build(); assertEquals(TestServlet.class.getName(), webApplication.getServletRegistration("testServlet").getClassName()); } /** * Test servletInitParam method. */ @Test void testServletInitParam() { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("testServlet", TestServlet.class) .servletInitParam("testServlet", "name", "value") .build(); assertEquals("value", webApplication.getServletRegistration("testServlet").getInitParameter("name")); } /** * Test servletMapping method. */ @Test void testServletMapping() { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("testServlet", TestServlet.class) .servletMapping("testServlet", "/test") .build(); assertEquals("/test", webApplication.getServletRegistration("testServlet").getMappings().iterator().next()); } /** * A test filter. */ public class TestFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { } } /** * A test servlet. */ public class TestServlet extends HttpServlet { } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/DefaultWebApplicationClassLoaderTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; /** * The JUnit tests for the DefaultWebApplicationClassLoader. * * @author Manfred Riem (mriem@manorrock.com) */ class DefaultWebApplicationClassLoaderTest { /** * Test loadClass method. * * @throws Exception when a serious error occurs. */ @Test void testLoadClass() throws Exception { DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(); assertNotNull(classLoader.loadClass("java.lang.String", true)); } /** * Test loadClass method. * * @throws Exception when a serious error occurs. */ @Test void testLoadClass2() throws Exception { DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(); assertThrows(ClassNotFoundException.class, () -> classLoader.loadClass("this.is.a.bogus.className", true)); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/DefaultWebApplicationExtensionContextTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import java.util.Set; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import org.junit.jupiter.api.Test; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import cloud.piranha.core.api.WebApplicationExtensionContext; /** * The JUnit tests for the DefaultWebApplicationExtensionContext. * * @author Manfred Riem (mriem@manorrock.com) */ class DefaultWebApplicationExtensionContextTest { /** * Test add method. */ @Test void testAdd() { DefaultWebApplicationExtensionContext context = new DefaultWebApplicationExtensionContext(); DefaultWebApplication webApplication = new DefaultWebApplication(); context.add(TestExtension.class); context.configure(webApplication); webApplication.initialize(); assertNotNull(webApplication.getAttribute(TestInitializer.class.getName())); } /** * Test remove method. */ @Test void testRemove() { DefaultWebApplicationExtensionContext context = new DefaultWebApplicationExtensionContext(); DefaultWebApplication webApplication = new DefaultWebApplication(); context.add(Test3Extension.class); context.configure(webApplication); webApplication.initialize(); assertNull(webApplication.getAttribute(TestInitializer.class.getName())); } /** * A test extension. */ public static class TestExtension implements WebApplicationExtension { @Override public void configure(WebApplication webApplication) { webApplication.addInitializer(TestInitializer.class.getName()); } } /** * A test extension. */ public static class Test2Extension implements WebApplicationExtension { /** * Extend the web application. * * @param context the context. */ @Override public void extend(WebApplicationExtensionContext context) { context.remove(TestExtension.class); } } /** * A test extension. */ public static class Test3Extension implements WebApplicationExtension { /** * Extend the web application. * * @param context the context. */ @Override public void extend(WebApplicationExtensionContext context) { context.add(TestExtension.class); context.add(Test2Extension.class); } } /** * A test servlet container initializer. */ public static class TestInitializer implements ServletContainerInitializer { public TestInitializer() { } @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { servletContext.setAttribute(TestInitializer.class.getName(), true); } } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/DefaultWebApplicationManagerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WelcomeFileManager; import java.util.List; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * The JUnit tests for the DefaultWebApplicationManager class. * * @author Manfred Riem (mriem@manorrock.com) */ class DefaultWebApplicationManagerTest { /** * Test getAnnotationManager method. */ @Test void testGetAnnotationManager() { DefaultWebApplicationManager manager = new DefaultWebApplicationManager(); assertNull(manager.getAnnotationManager()); manager.setAnnotationManager(new DefaultAnnotationManager()); assertNotNull(manager.getAnnotationManager()); } /** * Test getAsyncManager method. */ @Test void testGetAsyncManager() { DefaultWebApplicationManager manager = new DefaultWebApplicationManager(); assertNotNull(manager.getAsyncManager()); manager.setAsyncManager(null); assertNull(manager.getAsyncManager()); } /** * Test getDispatcherManager method */ @Test void testGetDispatcherManager() { DefaultWebApplicationManager manager = new DefaultWebApplicationManager(); assertNotNull(manager.getDispatcherManager()); } /** * Test getErrorPageManager method. */ @Test void testGetErrorPageManager() { DefaultWebApplicationManager manager = new DefaultWebApplicationManager(); assertNotNull(manager.getErrorPageManager()); manager.setErrorPageManager(null); assertNull(manager.getErrorPageManager()); } /** * Test getHttpSessionManager method. */ @Test void testGetHttpSessionManager() { DefaultWebApplicationManager manager = new DefaultWebApplicationManager(); manager.setHttpSessionManager(null); assertNull(manager.getHttpSessionManager()); } /** * Test getHttpSessionManager method. */ @Test void testGetHttpSessionManager2() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.getManager().getHttpSessionManager()); } /** * Test getJspManager method. */ @Test void testGetJspManager() { DefaultWebApplicationManager manager = new DefaultWebApplicationManager(); assertNotNull(manager.getJspManager()); manager.setJspManager(null); assertNull(manager.getJspManager()); } /** * Test getLocaleEncodingManager. */ @Test void testGetLocaleEncodingManager() { DefaultWebApplicationManager manager = new DefaultWebApplicationManager(); assertNotNull(manager.getLocaleEncodingManager()); manager.setLocaleEncodingManager(null); assertNull(manager.getLocaleEncodingManager()); } /** * Test getMultiPartManager method. */ @Test void testGetMultiPartManager() { DefaultWebApplicationManager manager = new DefaultWebApplicationManager(); assertNotNull(manager.getMultiPartManager()); manager.setMultiPartManager(null); assertNull(manager.getMultiPartManager()); } /** * Test getObjectInstanceManager method. */ @Test void testGetObjectInstanceManager() { DefaultWebApplicationManager manager = new DefaultWebApplicationManager(); assertNotNull(manager.getObjectInstanceManager()); manager.setObjectInstanceManager(null); assertNull(manager.getObjectInstanceManager()); } /** * Test getResourceManager method. */ @Test void testGetResourceManager() { DefaultWebApplicationManager manager = new DefaultWebApplicationManager(); assertNotNull(manager.getResourceManager()); manager.setResourceManager(null); assertNull(manager.getResourceManager()); } /** * Test getSecurityManager method. */ @Test void testGetSecurityManager() { DefaultWebApplicationManager manager = new DefaultWebApplicationManager(); assertNotNull(manager.getSecurityManager()); manager.setSecurityManager(null); assertNull(manager.getSecurityManager()); } /** * Test getServletRequestManager method. */ @Test void testGetServletRequestManager() { DefaultWebApplicationManager manager = new DefaultWebApplicationManager(); assertNotNull(manager.getServletRequestManager()); manager.setServletRequestManager(null); assertNull(manager.getServletRequestManager()); } /** * Test getWelcomeFileManager method. */ @Test void testGetWelcomeFileManager() { DefaultWebApplicationManager manager = new DefaultWebApplicationManager(); assertNull(manager.getWelcomeFileManager()); manager.setWelcomeFileManager(new WelcomeFileManager() { @Override public void addWelcomeFile(String welcomeFile) { } @Override public List getWelcomeFileList() { return null; } }); assertNotNull(manager.getWelcomeFileManager()); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/DefaultWebApplicationOutputStreamTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.WriteListener; import static jakarta.servlet.http.HttpServletResponse.SC_SWITCHING_PROTOCOLS; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the DefaultWebApplicationResponse class. * * @author Manfred Riem (mriem@manorrock.com) */ class DefaultWebApplicationOutputStreamTest { /** * Test isReady method. * * @throws Exception when a serious error occurs. */ @Test void testIsReady() throws Exception { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); ServletOutputStream outputStream = response.getOutputStream(); assertTrue(outputStream.isReady()); } /** * Test setWriteListener method. */ @Test void testWriteListener() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.setUpgraded(true); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setStatus(SC_SWITCHING_PROTOCOLS); webApplication.linkRequestAndResponse(request, response); response.setWebApplication(webApplication); ServletOutputStream outputStream = response.getOutputStream(); outputStream.setWriteListener(new WriteListener() { @Override public void onError(Throwable t) { } @Override public void onWritePossible() throws IOException { } }); assertNotNull(response.getWebApplicationOutputStream().getWriteListener()); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/DefaultWebApplicationRequestMapperTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Collection; import java.util.Set; import org.junit.jupiter.api.Test; /** * The JUnit tests for the DefaultWebApplicationRequestMapper class. * * @author Manfred Riem (mriem@manorrock.com) */ class DefaultWebApplicationRequestMapperTest { /** * Test addServletMaping method. * *

* Validate passing null pattern throws an IllegalArgumentException. *

*/ @Test void testAddServletMapping2() { DefaultWebApplicationRequestMapper mapper = new DefaultWebApplicationRequestMapper(); assertThrows(IllegalArgumentException.class, () -> {mapper.addServletMapping("kaboom", (String[]) null);}); } /** * Test addServletMapping method. * *

* Validate that we return the Servlet name(s) that already has / have been * mapped to the given URL pattern(s). *

*/ @Test void testAddServletMapping3() { DefaultWebApplicationRequestMapper mapper = new DefaultWebApplicationRequestMapper(); mapper.addServletMapping("first", "/mapped"); Set already = mapper.addServletMapping("second", "/mapped"); assertFalse(already.isEmpty()); } /** * Test addServletMapping method. * *

* Validate that we prepend a slash when it is missing. *

*/ @Test void testAddServletMapping4() { DefaultWebApplicationRequestMapper mapper = new DefaultWebApplicationRequestMapper(); mapper.addServletMapping("prepend", "mapped"); assertEquals("/mapped", mapper.findServletMapping("/mapped").getPattern()); } /** * Test addServletMapping method. * *

* Validate we set the default servlet for a "/" URL pattern. *

*/ @Test void testAddServletMapping5() { DefaultWebApplicationRequestMapper mapper = new DefaultWebApplicationRequestMapper(); mapper.addServletMapping("theDefaultServlet", "/"); assertEquals("theDefaultServlet", mapper.getDefaultServlet()); } /** * Test findServletMapping method. * *

* Test finding an exact match. *

*/ @Test void testFindServletMapping() { DefaultWebApplicationRequestMapper mapper = new DefaultWebApplicationRequestMapper(); mapper.addServletMapping("echo", "/echo"); DefaultWebApplicationRequestMapping mapping = mapper.findServletMapping("/echo"); assertTrue(mapping.isExact()); } /** * Test findExactServletMatch method. * * @throws Exception */ @Test void testFindExactServletMatch2() throws Exception { DefaultWebApplicationRequestMapper webAppRequestMapper = new DefaultWebApplicationRequestMapper(); DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setWebApplicationRequestMapper(webAppRequestMapper); webApplication.addServlet("echo", new TestEcho1Servlet()); webApplication.addServletMapping("echo", "/echo"); webApplication.initialize(); webApplication.start(); TestWebApplicationRequest request = new TestWebApplicationRequest(); request.setServletPath("/echo"); request.setWebApplication(webApplication); TestWebApplicationResponse response = new TestWebApplicationResponse(); response.setWebApplication(webApplication); response.setBodyOnly(true); webApplication.service(request, response); assertEquals("ECHO", new String(response.getResponseBytes())); } /** * Test findExtensionServletMatch method. * * @throws Exception */ @Test void testFindExtensionServletMatch() throws Exception { DefaultWebApplicationRequestMapper webAppRequestMapper = new DefaultWebApplicationRequestMapper(); DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setWebApplicationRequestMapper(webAppRequestMapper); webApplication.addServlet("echo", new TestEcho1Servlet()); webApplication.addServletMapping("echo", "*.echo"); webApplication.initialize(); webApplication.start(); TestWebApplicationRequest request = new TestWebApplicationRequest(); request.setServletPath("/echo.echo"); request.setWebApplication(webApplication); TestWebApplicationResponse response = new TestWebApplicationResponse(); response.setBodyOnly(true); response.setWebApplication(webApplication); webApplication.service(request, response); assertEquals("ECHO", new String(response.getResponseBytes())); } /** * Test findServletPrefixMatch method. * * @throws Exception */ @Test void testFindPrefixServletMatch3() throws Exception { DefaultWebApplicationRequestMapper webAppRequestMapper = new DefaultWebApplicationRequestMapper(); DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setWebApplicationRequestMapper(webAppRequestMapper); webApplication.addServlet("echo", new TestEcho1Servlet()); webApplication.addServletMapping("echo", "/echo/*"); webApplication.initialize(); webApplication.start(); TestWebApplicationRequest request = new TestWebApplicationRequest(); request.setServletPath("/echo2/test.echo"); request.setWebApplication(webApplication); TestWebApplicationResponse response = new TestWebApplicationResponse(); response.setBodyOnly(true); response.setWebApplication(webApplication); webApplication.service(request, response); assertEquals(404, response.getStatus()); } /** * Test findServletPrefixMatch method. * * @throws Exception */ @Test void testFindPrefixServletMatch4() throws Exception { DefaultWebApplicationRequestMapper webAppRequestMapper = new DefaultWebApplicationRequestMapper(); DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setWebApplicationRequestMapper(webAppRequestMapper); webApplication.addServlet("echo", new TestEcho1Servlet()); webApplication.addServletMapping("echo", "/echo/*"); webApplication.initialize(); webApplication.start(); TestWebApplicationRequest request = new TestWebApplicationRequest(); request.setServletPath("/echo"); request.setWebApplication(webApplication); TestWebApplicationResponse response = new TestWebApplicationResponse(); response.setBodyOnly(true); response.setWebApplication(webApplication); webApplication.service(request, response); assertEquals("ECHO", new String(response.getResponseBytes())); } @Test void testFindPrefixServletMatch5() throws Exception { DefaultWebApplicationRequestMapper webAppRequestMapper = new DefaultWebApplicationRequestMapper(); DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setWebApplicationRequestMapper(webAppRequestMapper); webApplication.addServlet("echo", new TestEcho1Servlet()); webApplication.addServletMapping("echo", "/echo/*"); webApplication.initialize(); webApplication.start(); TestWebApplicationRequest request = new TestWebApplicationRequest(); request.setServletPath("/echo2"); request.setWebApplication(webApplication); TestWebApplicationResponse response = new TestWebApplicationResponse(); response.setBodyOnly(true); response.setWebApplication(webApplication); webApplication.service(request, response); assertEquals(404, response.getStatus()); } /** * Test findExtensionServletMatch method. * * @throws Exception */ @Test void testFindExtensionServletMatch2() throws Exception { DefaultWebApplicationRequestMapper webAppRequestMapper = new DefaultWebApplicationRequestMapper(); DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setWebApplicationRequestMapper(webAppRequestMapper); webApplication.addServlet("echo2", new TestEcho1Servlet()); webApplication.addServletMapping("echo2", "*.echo2"); webApplication.initialize(); webApplication.start(); TestWebApplicationRequest request = new TestWebApplicationRequest(); request.setServletPath("/echo.echo"); request.setWebApplication(new DefaultWebApplication()); TestWebApplicationResponse response = new TestWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.service(request, response); assertNotEquals(200, response.getStatus()); } /** * Test findPrefixServletMatch method. * * @throws Exception */ @Test void testFindPrefixServletMatch() throws Exception { DefaultWebApplicationRequestMapper webAppRequestMapper = new DefaultWebApplicationRequestMapper(); DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setWebApplicationRequestMapper(webAppRequestMapper); webApplication.addServlet("echo", new TestEcho1Servlet()); webApplication.addServletMapping("echo", "/echo/*"); webApplication.initialize(); webApplication.start(); TestWebApplicationRequest request = new TestWebApplicationRequest(); request.setServletPath("/echo/test.echo"); request.setWebApplication(webApplication); TestWebApplicationResponse response = new TestWebApplicationResponse(); response.setBodyOnly(true); response.setWebApplication(webApplication); webApplication.service(request, response); assertEquals("ECHO", new String(response.getResponseBytes())); } /** * Test findPrefixServletMatch method. * * @throws Exception */ @Test void testFindPrefixServletMatch2() throws Exception { DefaultWebApplicationRequestMapper webAppRequestMapper = new DefaultWebApplicationRequestMapper(); DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setWebApplicationRequestMapper(webAppRequestMapper); webApplication.addServlet("echo", new TestEcho1Servlet()); webApplication.addServletMapping("echo", "/*"); webApplication.initialize(); webApplication.start(); TestWebApplicationRequest request = new TestWebApplicationRequest(); request.setServletPath("/echo/test.echo"); request.setWebApplication(webApplication); TestWebApplicationResponse response = new TestWebApplicationResponse(); response.setBodyOnly(true); response.setWebApplication(webApplication); webApplication.service(request, response); assertEquals("ECHO", new String(response.getResponseBytes())); } /** * Test addFilterMapping method. */ @Test void testAddFilterMapping() { DefaultWebApplicationRequestMapper requestMapper = new DefaultWebApplicationRequestMapper(); Set result = requestMapper.addFilterMapping("filter", "/*"); assertTrue(result.isEmpty()); Collection filters = requestMapper.findFilterMappings("/one_two"); assertFalse(filters.isEmpty()); } /** * Test addFilterMapping method. */ @Test void testAddFilterMapping2() { DefaultWebApplicationRequestMapper requestMapper = new DefaultWebApplicationRequestMapper(); Set result = requestMapper.addFilterMapping("filter", "/exactly_this"); assertTrue(result.isEmpty()); Collection filters = requestMapper.findFilterMappings("/exactly_this"); assertFalse(filters.isEmpty()); } /** * Test addFilterMapping method. */ @Test void testAddFilterMapping3() { DefaultWebApplicationRequestMapper requestMapper = new DefaultWebApplicationRequestMapper(); Set result = requestMapper.addFilterMapping("filter", "*.html"); assertTrue(result.isEmpty()); Collection filters = requestMapper.findFilterMappings("/index.html"); assertFalse(filters.isEmpty()); } /** * Test addFilterMapping method. */ @Test void testAddFilterMapping4() { DefaultWebApplicationRequestMapper requestMapper = new DefaultWebApplicationRequestMapper(); Set result = requestMapper.addFilterMapping("filter", "*.html"); assertTrue(result.isEmpty()); result = requestMapper.addFilterMapping("filter", "*.html"); assertFalse(result.isEmpty()); Collection filters = requestMapper.findFilterMappings("/index.html"); assertFalse(filters.isEmpty()); } /** * Test addFilterMapping method. */ @Test void testAddFilterMapping5() { DefaultWebApplicationRequestMapper requestMapper = new DefaultWebApplicationRequestMapper(); Set result = requestMapper.addFilterMapping("filter", "*.html"); assertTrue(result.isEmpty()); result = requestMapper.addFilterMapping("filter", "*.html"); assertFalse(result.isEmpty()); Collection filters = requestMapper.findFilterMappings("/index.html?q=keyword"); assertFalse(filters.isEmpty()); } /** * Test addServletMapping method. */ @Test void testAddServletMapping() { DefaultWebApplicationRequestMapper requestMapper = new DefaultWebApplicationRequestMapper(); Set result = requestMapper.addServletMapping("servlet", "*.html"); assertTrue(result.isEmpty()); result = requestMapper.addServletMapping("servlet", "*.html"); assertTrue(result.isEmpty()); DefaultWebApplicationRequestMapping mapping = requestMapper.findServletMapping("/index.html?q=keyword"); assertNotNull(mapping); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/DefaultWebApplicationRequestMappingTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * The JUnit tests for the DefaultWebApplicationRequestMapping class. * * @author Manfred Riem (mriem@manorrock.com) */ class DefaultWebApplicationRequestMappingTest { /** * Test getMatchValue method. */ @Test void testGetMatchValue() { DefaultWebApplicationRequestMapping mapping = new DefaultWebApplicationRequestMapping(null); assertNull(mapping.getMatchValue()); mapping.setMatchValue("/match"); assertEquals("/match", mapping.getMatchValue()); } /** * Test getPattern method. */ @Test void testGetPattern() { DefaultWebApplicationRequestMapping mapping = new DefaultWebApplicationRequestMapping("/*"); assertEquals("/*", mapping.getPattern()); } /** * Test isExact method. */ @Test void testIsExact() { DefaultWebApplicationRequestMapping mapping = new DefaultWebApplicationRequestMapping("/exact"); assertFalse(mapping.isExact()); mapping.setExact(true); assertTrue(mapping.isExact()); } /** * Test isExtension method. */ @Test void testIsExtension() { DefaultWebApplicationRequestMapping mapping = new DefaultWebApplicationRequestMapping("*.html"); assertFalse(mapping.isExtension()); mapping.setExtension(true); assertTrue(mapping.isExtension()); } /** * Test setPattern method. */ @Test void testSetPattern() { DefaultWebApplicationRequestMapping mapping = new DefaultWebApplicationRequestMapping("/pattern1"); assertEquals("/pattern1", mapping.getPattern()); mapping.setPattern("/pattern2"); assertEquals("/pattern2", mapping.getPattern()); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/DefaultWebApplicationRequestTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplicationInputStream; import cloud.piranha.core.api.WebApplicationRequest; import static jakarta.servlet.DispatcherType.INCLUDE; import static jakarta.servlet.DispatcherType.REQUEST; import jakarta.servlet.ReadListener; import jakarta.servlet.ServletException; import jakarta.servlet.ServletInputStream; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpUpgradeHandler; import jakarta.servlet.http.WebConnection; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import static java.nio.charset.StandardCharsets.UTF_8; import java.util.Enumeration; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; /** * The JUnit tests for the DefaultWebApplicationRequest class. * * @author Manfred Riem (mriem@manorrock.com) */ class DefaultWebApplicationRequestTest { /** * Test authenticate method. */ @Test void testAuthenticate() { try { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); assertFalse(request.authenticate(response)); } catch (IOException | ServletException exception) { fail(); } } /** * Test authenticate method. */ @Test void testAuthenticate2() { try { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); request.authenticate(response); } catch (IOException | ServletException ex) { fail(); } } /** * Test getAsyncContext method. */ @Test void testGetAsyncContext() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNotNull(assertThrows(IllegalStateException.class, request::getAsyncContext)); } /** * Test getAsyncContext method. */ @Test void testGetAsyncContext2() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); request.setAsyncSupported(true); request.startAsync(); assertNotNull(request.getAsyncContext()); } /** * Test getAttribute method. */ @Test void testGetAttribute() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.setAttribute("name", "value"); assertNotNull(request.getAttribute("name")); } /** * Test getAttributeNames method. */ @Test void testGetAttributeNames() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.setAttribute("name", "value"); request.setAttribute("name2", "value2"); assertNotNull(request.getAttributeNames()); Enumeration attributeNames = request.getAttributeNames(); assertNotNull(attributeNames.nextElement()); assertNotNull(attributeNames.nextElement()); assertFalse(attributeNames.hasMoreElements()); } /** * Test getAuthType method. */ @Test void testGetAuthType() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getAuthType()); } /** * Test getCharacterEncoding method. * * @throws Exception when a serious error occurs. */ @Test void testGetCharacterEncoding() throws Exception { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getCharacterEncoding()); request.setCharacterEncoding("UTF-8"); assertEquals("UTF-8", request.getCharacterEncoding()); } /** * Test getContentLength method. */ @Test void testGetContentLength() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertEquals(-1, request.getContentLength()); } /** * Test getContentLength method. */ @Test void testGetContentLength2() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setContentLength(1234); assertEquals(1234, request.getContentLength()); assertEquals("1234", request.getHeader("Content-Length")); } /** * Test getContentLengthLong method. */ @Test void testGetContentLengthLong() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertEquals(-1L, request.getContentLengthLong()); } /** * Test getContentType method. */ @Test void testGetContentType() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getContentType()); } /** * Test getContentType method. */ @Test void testGetContentType2() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setContentType("text/html"); assertEquals("text/html", request.getContentType()); assertEquals("text/html", request.getHeader("Content-Type")); } /** * Test getContextPath method. */ @Test void testGetContextPath() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertEquals("", request.getContextPath()); } /** * Test getCookies method. */ @Test void testGetCookies() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getCookies()); } /** * Test getDateHeader method. */ @Test void testGetDateHeader() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertEquals(-1L, request.getDateHeader("notfound")); } /** * Test getDispatcherType. */ @Test void testGetDispatcherType() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertEquals(REQUEST, request.getDispatcherType()); } /** * Test getHeader method. */ @Test void testGetHeader() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getHeader("notfound")); } /** * Test getHeaderNames method. */ @Test void testGetHeaderNames() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNotNull(request.getHeaderNames()); } /** * Test getHeaders method. */ @Test void testGetHeaders() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNotNull(request.getHeaders("notfound")); } /** * Test getHttpServletMapping method. */ @Test void testGetHttpServletMapping() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNotNull(request.getHttpServletMapping()); } /** * Test getInputStream method. * * @throws Exception when a serious error occurs. */ @Test void testGetInputStream() throws Exception { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNotNull(request.getInputStream()); } /** * Test getInputStream method. * * @throws Exception when a serious error occurs. */ @Test void testGetInputStream2() throws Exception { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNotNull(request.getInputStream()); assertNotNull(assertThrows(IllegalStateException.class, request::getReader)); } /** * Test getIntHeader method. */ @Test void testGetIntHeader() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertEquals(-1, request.getIntHeader("notfound")); } /** * Test getLocalAddr method. */ @Test void testGetLocalAddr() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getLocalAddr()); } /** * Test getLocalName method. */ @Test void testGetLocalName() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getLocalName()); } /** * Test getLocalPort method. */ @Test void testGetLocalPort() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertEquals(0, request.getLocalPort()); } /** * Test getLocale method. */ @Test void testGetLocale() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setHeader("Accept-Language", "en"); assertNotNull(request.getLocale()); } /** * Test getLocale method. */ @Test void testGetLocale2() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNotNull(request.getLocale()); } /** * Test getLocale method. */ @Test void testGetLocales() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setHeader("Accept-Language", "en, de"); assertNotNull(request.getLocales()); } /** * Test getLocale method. */ @Test void testGetLocales2() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNotNull(request.getLocales()); } /** * Test getMethod method. */ @Test void testGetMethod() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertEquals("GET", request.getMethod()); } /** * Test getMultipartConfig method. */ @Test void testGetMultipartConfig() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getMultipartConfig()); } /** * Test getParameter method. */ @Test void testGetParameter() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getParameter("notfound")); } /** * Test getParameterMap method. */ @Test void testGetParameterMap() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); WebApplicationInputStream requestInput = request.getWebApplicationInputStream(); requestInput.setInputStream(new ByteArrayInputStream("param1=value1".getBytes(UTF_8))); request.setContentType("application/x-www-form-urlencoded"); Map parameterMap = request.getParameterMap(); assertEquals(1, parameterMap.size()); assertArrayEquals(new String[]{"value1"}, parameterMap.get("param1")); } /** * Test getParameterMap method. */ @Test void testGetParameterMap2() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); WebApplicationInputStream requestInput = request.getWebApplicationInputStream(); requestInput.setInputStream(new ByteArrayInputStream("param1=value1".getBytes(UTF_8))); request.setContentType("application/x-www-form-urlencoded;charset=UTF-8"); Map parameterMap = request.getParameterMap(); assertEquals(1, parameterMap.size()); assertArrayEquals(new String[]{"value1"}, parameterMap.get("param1")); } /** * Test getParameterMap. */ @Test void testGetParameterMap3() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNotNull(request.getParameterMap()); } /** * Test getParameterNames. */ @Test void testGetParameterNames() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNotNull(request.getParameterNames()); } /** * Test getParameterValues. */ @Test void testGetParameterValues() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getParameterValues("notfound")); } /** * Test getPart method. */ @Test void testGetPart() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setContentType("text/html"); assertNotNull(assertThrows(ServletException.class, () -> request.getPart("not_there"))); } /** * Test getPart method. * * @throws Exception when a serious error occurs. */ @Test void testGetPart2() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.setContentType("multipart/form-data"); assertNull(request.getPart("not_there")); } /** * Test getPart method. */ @Test void testGetPart3() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); assertThrows(ServletException.class, () -> { request.getPart("notfound"); }); } /** * Test getParts method. */ @Test void testGetParts() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); assertThrows(ServletException.class, request::getParts); } /** * Test getPathInfo method. */ @Test void testGetPathInfo() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getPathInfo()); } /** * Test getPathTranslated method. */ @Test void testGetPathTranslated() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getPathTranslated()); } /** * Test getProtocol method. */ @Test void testGetProtocol() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertEquals("HTTP/1.1", request.getProtocol()); } /** * Test getProtocolRequestId method. */ @Test void testGetProtocolRequestId() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertEquals("", request.getProtocolRequestId()); } /** * Test getQueryString method. */ @Test void testGetQueryString() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getQueryString()); } /** * Test getReader method. * * @throws Exception when a serious error occurs. */ @Test void testGetReader() throws Exception { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNotNull(request.getReader()); } /** * Test getReader method. * * @throws Exception when a serious error occurs. */ @Test void testGetReader2() throws Exception { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNotNull(request.getReader()); assertNotNull(assertThrows(IllegalStateException.class, () -> request.getInputStream())); } /** * Test getRemoteAddr method. */ @Test void testGetRemoteAddr() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getRemoteAddr()); } /** * Test getRemoteHost method. */ @Test void testGetRemoteHost() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getRemoteHost()); } /** * Test getRemotePort method. */ @Test void testGetRemotePort() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertEquals(0, request.getRemotePort()); } /** * Test getRemoteUser method. */ @Test void testGetRemoteUser() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getRemoteUser()); } /** * Test getRequestId method. */ @Test void testGetRequestId() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNotNull(request.getRequestId()); } /** * Test getRequestURI method. */ @Test void testGetRequestURI() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNotNull(request.getRequestURI()); } /** * Test getRequestURL method. */ @Test void testGetRequestURL() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNotNull(request.getRequestURL()); } /** * Test getRequestedSessionId method. */ @Test void testGetRequestedSessionId() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getRequestedSessionId()); } /** * Test getScheme method. */ @Test void testGetScheme() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertEquals("http", request.getScheme()); } /** * Test getServerName method. */ @Test void testGetServerName() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertEquals("localhost", request.getServerName()); } /** * Test getServerPort method. */ @Test void testGetServerPort() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertEquals(80, request.getServerPort()); } /** * Test getServletConnection method. */ @Test void testGetServletConnection() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNotNull(request.getServletConnection()); } /** * Test getServletContext method. */ @Test void testGetServletContext() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); assertNotNull(request.getServletContext()); } /** * Test getServletPath method. */ @Test void testGetServletPath() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertEquals("", request.getServletPath()); } /** * Test getTrailerFields method. */ @Test void testGetTrailerFields() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); Map trailerFields = request.getTrailerFields(); assertTrue(trailerFields.isEmpty()); } /** * Test getUpgradeHandler method. */ @Test void testGetUpgradeHandler() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getUpgradeHandler()); } /** * Test getUserPrincipal method. */ @Test void testGetUserPrincipal() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getUserPrincipal()); } /** * Test isAsyncStarted method. */ @Test void testIsAsyncStarted() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertFalse(request.isAsyncStarted()); } /** * Test isAsyncSupported method. */ @Test void testIsAsyncSupported() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertFalse(request.isAsyncSupported()); } /** * Test isSecure method. */ @Test void testIsSecure() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertFalse(request.isSecure()); } /** * Test isSecure method. */ @Test void testIsSecure2() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setScheme("https"); assertTrue(request.isSecure()); } /** * Test isTrailerFieldsReady method. */ @Test void testIsTrailerFieldsReady() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertTrue(request.isTrailerFieldsReady()); } /** * Test isUpgraded method. */ @Test void testIsUpgraded() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertFalse(request.isUpgraded()); } /** * Test isUserInRole method. */ @Test void testIsUserInRole() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); assertFalse(request.isUserInRole("notmatched")); } /** * Test login method. */ @Test void testLogin() { try { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.login("admin", "password"); fail(); } catch (ServletException exception) { } } /** * Test logout method. */ @Test void testLogout() { try { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.logout(); assertNull(request.getAuthType()); assertNull(request.getRemoteUser()); assertNull(request.getUserPrincipal()); } catch (Exception exception) { fail(); } } /** * Test newPushBuilder method. */ @SuppressWarnings("deprecation") @Test void testNewPushBuilder() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.newPushBuilder()); } /** * Test removeAttribute method. */ @Test void testRemoveAttribute() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.setAttribute("name", "value"); assertNotNull(request.getAttribute("name")); request.removeAttribute("name"); assertNull(request.getAttribute("name")); } /** * Test setAttribute method. */ @Test void testSetAttribute() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.setAttribute("name", "value"); assertNotNull(request.getAttribute("name")); } /** * Test setAuthType method. */ @Test void testSetAuthType() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.setAuthType("MYTYPE"); assertEquals("MYTYPE", request.getAuthType()); } /** * Test setCharacterEncoding method. * * @throws Exception when a serious error occurs. */ @Test void testSetCharacterEncoding() throws Exception { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getCharacterEncoding()); request.setCharacterEncoding("UTF-8"); assertEquals("UTF-8", request.getCharacterEncoding()); } /** * Test setCharacterEncoding method. * * @throws Exception when a serious error occurs. */ @Test void testSetCharacterEncoding2() throws Exception { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getCharacterEncoding()); request.getReader(); request.setCharacterEncoding("UTF-8"); assertNotEquals("UTF-8", request.getCharacterEncoding()); } /** * Test setCharacterEncoding method. * */ @Test void testSetCharacterEncoding3() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNotNull(assertThrows(UnsupportedEncodingException.class, () -> request.setCharacterEncoding("doesnotexist"))); } /** * Test setCharacterEncoding method. * */ @Test void testSetCharacterEncoding4() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNotNull(assertThrows(UnsupportedEncodingException.class, () -> request.setCharacterEncoding((String) null))); } /** * Test setContextPath method. */ @Test void testSetContextPath() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setContextPath("/contextPath"); assertEquals("/contextPath", request.getContextPath()); } /** * Test setCookies method. */ @Test void testSetCookies() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getCookies()); request.setCookies(new Cookie[0]); assertNull(request.getCookies()); Cookie[] cookies = new Cookie[1]; cookies[0] = new Cookie("name", "value"); request.setCookies(cookies); assertNotNull(request.getCookies()); assertEquals("name", request.getCookies()[0].getName()); assertEquals("value", request.getCookies()[0].getValue()); } /** * Test setDispatcherType method. */ @Test void testSetDispatcherType() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setDispatcherType(INCLUDE); assertEquals(INCLUDE, request.getDispatcherType()); } /** * Test setLocalAddr method. */ @Test void testSetLocalAddr() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getLocalAddr()); request.setLocalAddr("127.0.0.1"); assertEquals("127.0.0.1", request.getLocalAddr()); } /** * Test setLocalName method. */ @Test void testSetLocalName() throws Exception { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getLocalName()); request.setLocalName("localhost"); assertEquals("localhost", request.getLocalName()); } /** * Test setLocalPort method. */ @Test void testSetLocalPort() throws Exception { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertEquals(0, request.getLocalPort()); request.setLocalPort(12345); assertEquals(12345, request.getLocalPort()); } /** * Test setProtocol method. */ @Test void testSetProtocol() throws Exception { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertEquals("HTTP/1.1", request.getProtocol()); request.setProtocol("HTTP/1.0"); assertEquals("HTTP/1.0", request.getProtocol()); } /** * Test setRemoteAddr method. */ @Test void testSetRemoteAddr() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getRemoteAddr()); request.setRemoteAddr("127.0.0.1"); assertEquals("127.0.0.1", request.getRemoteAddr()); } /** * Test setRemoteHost method. */ @Test void testSetRemoteHost() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNull(request.getRemoteHost()); request.setRemoteHost("localhost"); assertEquals("localhost", request.getRemoteHost()); } /** * Test setRemotePort method. */ @Test void testSetRemotePort() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertEquals(0, request.getRemotePort()); request.setRemotePort(12345); assertEquals(12345, request.getRemotePort()); } /** * Test setServerName method. */ @Test void testSetServerName() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertEquals("localhost", request.getServerName()); request.setServerName("my.host.com"); assertEquals("my.host.com", request.getServerName()); } /** * Test setServerPort method. */ @Test void testSetServerPort() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertEquals(80, request.getServerPort()); request.setServerPort(8080); assertEquals(8080, request.getServerPort()); } /** * Test setServletPath method. */ @Test void testSetServletPath() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setServletPath("/servletPath"); assertEquals("/servletPath", request.getServletPath()); } /** * Test setUserPrincipal method. */ @Test void testSetUserPrincipal() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setUserPrincipal(() -> { return "User Principal"; }); assertEquals("User Principal", request.getUserPrincipal().getName()); } /** * Test setWebApplication method. */ @Test void testSetWebApplication() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); assertEquals(webApplication, request.getServletContext()); } /** * Test startAsync method. */ @Test void testStartAsync() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertThrows(IllegalStateException.class, request::startAsync); } /** * Test startAsync method. */ @Test void testStartAsync2() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); assertThrows(IllegalStateException.class, () -> { request.startAsync(request, response); }); } /** * Test startAsync method. */ @Test void testStartAsync3() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.setAsyncSupported(true); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); assertNotNull(request.startAsync()); } /** * Test startAsync method. */ @Test void testStartAsync4() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setAsyncSupported(false); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); assertNotNull(assertThrows(IllegalStateException.class, () -> request.startAsync(request, response))); } /** * Test upgrade method. */ @Test void testUpgrade() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addServlet("Upgrade", new HttpServlet() { @Override public void doPost( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (request.getHeader("Upgrade") != null) { response.setStatus(101); response.setHeader("Upgrade", "YES"); response.setHeader("Connection", "Upgrade"); request.upgrade(TestUpgradeHttpUpgradeHandler.class); } else { response.getWriter().println("Not upgraded"); } } }); webApplication.addServletMapping("Upgrade", "/*"); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.setHeader("Upgrade", "YES"); request.setMethod("POST"); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); response.getWebApplicationOutputStream().setOutputStream(byteOutput); webApplication.service(request, response); assertEquals(101, response.getStatus()); } /** * Test upgrade method. * * @throws Exception when a serious error occurs. */ @Test void testUpgrade2() throws Exception { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); assertNotNull(request.upgrade(TestUpgrade2HttpUpgradeHandler.class)); } /** * Test upgrade method. */ @Test void testUpgrade3() { WebApplicationRequest request = new DefaultWebApplicationRequest(); assertNotNull(assertThrows(ServletException.class, () -> request.upgrade(TestUpgrade3HttpUpgradeHandler.class))); } /** * A HttpUpgradeHandler used by the testUpgrade1 method. */ public static class TestUpgrade2HttpUpgradeHandler implements HttpUpgradeHandler { /** * Constructor. */ public TestUpgrade2HttpUpgradeHandler() { } @Override public void destroy() { } @Override public void init(WebConnection conection) { try { ServletInputStream input = conection.getInputStream(); ServletOutputStream output = conection.getOutputStream(); TestUpgradeReadListener readListener = new TestUpgradeReadListener(input, output); input.setReadListener(readListener); output.flush(); } catch (Exception ex) { throw new RuntimeException(ex); } } } /** * A HttpUpgradeHandler used by testUpgrade3 method. */ public static class TestUpgrade3HttpUpgradeHandler implements HttpUpgradeHandler { /** * Constructor. * * @throws IllegalAccessException on purpose. */ public TestUpgrade3HttpUpgradeHandler() throws IllegalAccessException { throw new IllegalAccessException(); } @Override public void init(WebConnection wc) { } @Override public void destroy() { } } /** * A HttpUpgradeHandler used by the testUpgrade2 method. */ public static class TestUpgradeHttpUpgradeHandler implements HttpUpgradeHandler { /** * Constructor. */ public TestUpgradeHttpUpgradeHandler() { } @Override public void init(WebConnection wc) { } @Override public void destroy() { } } /** * A ReadListener used by the testUpgrade method. */ public static class TestUpgradeReadListener implements ReadListener { /** * Stores the input stream. */ private ServletInputStream inputStream = null; /** * Stores the output stream. */ private ServletOutputStream outputStream = null; /** * Stores the DELIMITER constant. */ private static final String DELIMITER = "/"; /** * Constructor. * * @param inputStream the input steam. * @param outputStream the output stream. */ public TestUpgradeReadListener(ServletInputStream inputStream, ServletOutputStream outputStream) { this.inputStream = inputStream; this.outputStream = outputStream; } @Override public void onDataAvailable() { try { StringBuilder sb = new StringBuilder(); int len = -1; byte b[] = new byte[1024]; while (inputStream.isReady() && (len = inputStream.read(b)) != -1) { String data = new String(b, 0, len); sb.append(data); } outputStream.println(DELIMITER + sb.toString()); outputStream.flush(); } catch (Exception ex) { throw new IllegalStateException(ex); } } @Override public void onAllDataRead() { try { outputStream.close(); } catch (Exception ex) { throw new IllegalStateException(ex); } } @Override public void onError(final Throwable t) { t.printStackTrace(); } } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/DefaultWebApplicationResponseTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplicationOutputStream; import cloud.piranha.core.api.WebApplicationResponse; import jakarta.servlet.ServletException; import jakarta.servlet.WriteListener; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.Locale; import static java.util.Locale.GERMAN; import static java.util.Locale.ITALIAN; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * The JUnit tests for the DefaultWebApplicationResponse class. * * @author Manfred Riem (mriem@manorrock.com) */ class DefaultWebApplicationResponseTest { /** * Test addCookie method. */ @Test void testAddCookie() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.addCookie(new Cookie("name", "value")); assertFalse(response.getCookies().isEmpty()); } /** * Test addDateHeader method. */ @Test void testAddDateHeader() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.addDateHeader("name", 1234); assertEquals("Thu, 1 Jan 1970 00:00:01 GMT", response.getHeader("name")); } /** * Test addHeader method. */ @Test void testAddHeader() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); assertFalse(response.containsHeader("name")); response.addHeader("name", "value"); assertTrue(response.containsHeader("name")); assertEquals("value", response.getHeader("name")); } /** * Test addIntHeader method. */ @Test void testAddIntHeader() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.addIntHeader("name", 1234); assertEquals("1234", response.getHeader("name")); } /** * Test addIntHeader method. */ @Test void testAddIntHeader2() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.addIntHeader("name", 1234); assertEquals("1234", response.getHeader("name")); response.addIntHeader("name", 2345); Collection values = response.getHeaders("name"); assertFalse(values.isEmpty()); assertTrue(values.contains("1234")); assertTrue(values.contains("2345")); } /** * Test closeAsyncResponse method. */ @Test void testCloseAsyncResponse() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setResponseCloser(() -> { }); response.closeAsyncResponse(); } /** * Test containsHeader method. */ @Test void testContainsHeader() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); assertFalse(response.containsHeader("name")); response.addHeader("name", "value"); assertTrue(response.containsHeader("name")); } /** * Test encodeRedirectUrl method. */ @Test void testEncodeRedirectUrl() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); assertNotNull(response.encodeRedirectURL("/encodeMe")); } /** * Test encodeUrl method. */ @Test void testEncodeUrl() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); assertNotNull(response.encodeURL("/encodeMe")); } /** * Test flushBuffer method. */ @Test void testFlushBuffer() { try { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.flushBuffer(); } catch (IOException ex) { fail(); } } /** * Test flushBuffer method. * * @throws Exception when a serious error occurs. */ @Test void testFlushBuffer2() throws Exception { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setContentLength(20); PrintWriter writer = response.getWriter(); writer.print("0123456789"); writer.print("0123456789"); writer.print("0123456789"); writer.print("0123456789"); writer.println("And we flushed!"); response.addIntHeader("header1", 12345); assertNull(response.getHeader("header1")); } /** * Test getBufferSize method. */ @Test void testGetBufferSize() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); assertTrue(response.getBufferSize() >= 0); } /** * Test getCharacterEncoding method. */ @Test void testGetCharacterEncoding() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); assertEquals("ISO-8859-1", response.getCharacterEncoding()); response.setCharacterEncoding("UTF-8"); assertEquals("UTF-8", response.getCharacterEncoding()); } /** * Test getContentLanguage method. */ @Test void testGetContentLanguage() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); assertNull(response.getContentLanguage()); } /** * Test getContentType method. */ @Test void testGetContentType() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); assertNull(response.getContentType()); response.setContentType("text/html"); assertEquals("text/html", response.getContentType()); } /** * Test getContentType method. */ @Test void testGetContentType2() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); assertNull(response.getContentType()); response.setContentType("text/html;charset=UTF-8"); assertEquals("text/html;charset=UTF-8", response.getContentType()); assertEquals("UTF-8", response.getCharacterEncoding()); } /** * Test getCookies method. */ @Test void testGetCookies() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.addCookie(new Cookie("name", "value")); assertFalse(response.getCookies().isEmpty()); } /** * Test getHeader method. */ @Test void testGetHeader() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.addHeader("name", "value"); assertEquals("value", response.getHeader("name")); } /** * Test getHeaderNames method. */ @Test void testGetHeaderNames() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); assertNotNull(response.getHeaderNames()); assertTrue(response.getHeaderNames().isEmpty()); response.addHeader("name", "value"); assertFalse(response.getHeaderNames().isEmpty()); } /** * Test getHeaders method. */ @Test void testGetHeaders() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); assertNotNull(response.getHeaders("name")); assertTrue(response.getHeaders("name").isEmpty()); response.addHeader("name", "value"); assertFalse(response.getHeaders("name").isEmpty()); } /** * Test getLocale method. */ @Test void testGetLocale() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); assertEquals(Locale.getDefault(), response.getLocale()); response.setLocale(ITALIAN); assertEquals(ITALIAN, response.getLocale()); } /** * Test getOutputStream method. * * @throws Exception when a serious error occurs. */ @Test void testGetOutputStream() throws Exception { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); assertNotNull(response.getOutputStream()); } /** * Test getOutputStream method. * * @throws Exception when a serious error occurs. */ @Test void testGetOutputStream2() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.getWriter(); assertNotNull(assertThrows(IllegalStateException.class, response::getOutputStream)); } /** * Test getResponseCloser method. */ @Test void testGetResponseCloser() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); assertNotNull(response.getResponseCloser()); } /** * Test getStatus method. */ @Test void testGetStatus() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); assertEquals(200, response.getStatus()); } /** * Test getStatusMessage method. */ @Test void testGetStatusMessage() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); assertNull(response.getStatusMessage()); } /** * Test getTrailerFields method. */ @Test void testGetTrailerFields() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); assertNull(response.getTrailerFields()); } /** * Test getWebApplication method. */ @Test void testGetWebApplication() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); assertNull(response.getWebApplication()); } /** * Test getWebApplicationOutputStream method. */ @Test void testGetWebApplicationOutputStream() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); assertNotNull(response.getWebApplicationOutputStream()); } /** * Test getWriter method. * * @throws Exception when a serious error occurs. */ @Test void testGetWriter() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); assertNotNull(response.getWriter()); } /** * Test getWriter method. * * @throws Exception when a serious error occurs. */ @Test void testGetWriter2() throws Exception { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.getOutputStream(); assertNotNull(assertThrows(IllegalStateException.class, response::getWriter)); } /** * Test isBodyOnly method. */ @Test void testIsBodyOnly() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); assertFalse(response.isBodyOnly()); } /** * Test isBufferResetting method. * * @throws Exception when a serious error occurs. */ @Test void testIsBufferResetting() throws Exception { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); assertFalse(response.isBufferResetting()); } /** * Test isCommitted method. * * @throws Exception when a serious error occurs. */ @Test void testIsCommitted() throws IOException { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); assertFalse(response.isCommitted()); response.flushBuffer(); assertTrue(response.isCommitted()); } /** * Test reset method. */ @Test void testReset() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.reset(); assertEquals(200, response.getStatus()); } /** * Test reset method. * */ @Test void testReset2() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setCharacterEncoding("UTF-8"); assertEquals("UTF-8", response.getCharacterEncoding()); response.reset(); assertNotEquals("UTF-8", response.getCharacterEncoding()); assertEquals("ISO-8859-1", response.getCharacterEncoding()); } /** * Test resetBuffer method. * * @throws Exception when a serious error occurs. */ @Test void testResetBuffer() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setCharacterEncoding("UTF-8"); response.resetBuffer(); assertEquals("UTF-8", response.getCharacterEncoding()); response.flushBuffer(); try { response.resetBuffer(); fail(); } catch (IllegalStateException ise) { } } /** * Test sendError method. * * @throws Exception when a serious error occurs. */ @Test void testSendError() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.sendError(SC_INTERNAL_SERVER_ERROR, "error"); response.flushBuffer(); assertEquals(SC_INTERNAL_SERVER_ERROR, response.getStatus()); assertTrue(response.isCommitted()); } /** * Test sendError method. * * @throws Exception when a serious error occurs. */ @Test void testSendError2() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.sendError(SC_INTERNAL_SERVER_ERROR, "error"); response.flushBuffer(); assertNotNull(assertThrows(IllegalStateException.class, () -> response.sendError(SC_INTERNAL_SERVER_ERROR))); } /** * Test sendRedirect method. * */ @Test void testSendRedirect() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addServlet("Servlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.flushBuffer(); response.sendRedirect("/this_should_throw_an_illegal_state_exception"); } }); webApplication.addServletMapping("Servlet", "/servlet"); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.setServletPath("/servlet"); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); assertNotNull(assertThrows(IllegalStateException.class, () -> webApplication.service(request, response))); } /** * Test sendRedirect method. * * @throws Exception when a serious error occurs. */ @Test void testSendRedirect2() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addServlet("Servlet2a", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.sendRedirect("servlet2b"); } }); webApplication.addServlet("Servlet2b", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setStatus(200); PrintWriter writer = response.getWriter(); writer.print("SUCCESS"); writer.flush(); } }); webApplication.addServletMapping("Servlet2a", "/servlet2a"); webApplication.addServletMapping("Servlet2b", "/servlet2a/servlet2b"); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setServletPath("/servlet2a"); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.service(request, response); assertEquals(302, response.getStatus()); assertNotNull(response.getHeader("Location")); } /** * Test sendRedirect method. * * @throws Exception when a serious error occurs. */ @Test void testRedirect3() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addServlet("Servlet3", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.sendRedirect("/relative_to_root"); } }); webApplication.addServletMapping("Servlet3", "/servlet3"); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setServletPath("/servlet3"); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.service(request, response); assertEquals(302, response.getStatus()); assertNotNull(response.getHeader("Location")); } /** * Test sendRedirect method. * * @throws Exception when a serious error occurs. */ @Test void testSendRedirect4() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addServlet("Servlet4", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.sendRedirect("http://this.is.outside/and_absolute"); } }); webApplication.addServletMapping("Servlet4", "/servlet4"); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setServletPath("/servlet4"); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.service(request, response); assertEquals(302, response.getStatus()); assertNotNull(response.getHeader("Location")); assertEquals("http://this.is.outside/and_absolute", response.getHeader("Location")); } /** * Test sendRedirect method. * * @throws Exception when a serious error occurs. */ @Test void testSendRedirect5() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addServlet("Servlet5a", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.sendRedirect("servlet5b"); } }); webApplication.addServlet("Servlet5b", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setStatus(200); PrintWriter writer = response.getWriter(); writer.print("SUCCESS"); writer.flush(); } } ); webApplication.setContextPath("/app"); webApplication.addServletMapping("Servlet5a", "/servlet5a"); webApplication.addServletMapping("Servlet5b", "/servlet5a/servlet5b"); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.setContextPath("/app"); request.setServletPath("/servlet5a"); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.service(request, response); assertEquals(302, response.getStatus()); assertNotNull(response.getHeader("Location")); } /** * Test setBodyOnly method. */ @Test void testSetBodyOnly() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); assertFalse(response.isBodyOnly()); response.setBodyOnly(true); assertTrue(response.isBodyOnly()); } /** * Test setBufferSize method. * * @throws Exception when a serious error occurs. */ @Test void testSetBufferSize() throws Exception { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setBufferSize(1024); assertEquals(1024, response.getBufferSize()); response.flushBuffer(); assertThrows(IllegalStateException.class, () -> response.setBufferSize(512)); } /** * Test setCharacterEncoding method. */ @Test void testSetCharacterEncoding() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); String defaultEncoding = response.getCharacterEncoding(); response.setCharacterEncoding("UTF-8"); assertTrue("UTF-8".equalsIgnoreCase(response.getCharacterEncoding())); response.setCharacterEncoding((String) null); assertTrue((defaultEncoding == null && response.getCharacterEncoding() == null) || (defaultEncoding != null && defaultEncoding.equalsIgnoreCase(response.getCharacterEncoding()))); } /** * Test setCharacterEncoding method. */ @Test void testSetCharacterEncoding2() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); String defaultEncoding = response.getCharacterEncoding(); response.setCharacterEncoding("UTF-8"); assertTrue("UTF-8".equalsIgnoreCase(response.getCharacterEncoding())); response.setCharacterEncoding((String) null); assertTrue((defaultEncoding == null && response.getCharacterEncoding() == null) || (defaultEncoding != null && defaultEncoding.equalsIgnoreCase(response.getCharacterEncoding()))); } /** * Test setCharacterEncoding method. */ @Test void testSetCharacterEncoding3() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setCharacterEncoding("does-not-exist"); assertTrue("does-not-exist".equalsIgnoreCase(response.getCharacterEncoding())); assertThrows(UnsupportedEncodingException.class, response::getWriter); } /** * Test setCharacterEncoding method. */ @Test void testSetCharacterEncoding4() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setContentType("text/html"); response.setCharacterEncoding("ISO-8859-7"); String contentType = response.getContentType(); assertTrue(contentType.toLowerCase().contains("text/html")); assertTrue(contentType.toLowerCase().contains("charset")); assertTrue(contentType.toLowerCase().contains("iso-8859-7")); } /** * Test setCharacterEncoding method. */ @Test void testSetCharacterEncoding5() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); String defaultCharacterEncoding = response.getCharacterEncoding(); response.setCharacterEncoding("UTF-8"); assertEquals("UTF-8", response.getCharacterEncoding()); response.setCharacterEncoding((String) null); assertEquals(defaultCharacterEncoding, response.getCharacterEncoding()); response.reset(); response.setContentType("text/plain; charset=UTF-8"); assertEquals("UTF-8", response.getCharacterEncoding()); response.setCharacterEncoding((String) null); assertEquals(defaultCharacterEncoding, response.getCharacterEncoding()); response.reset(); response.setCharacterEncoding("does-not-exist"); assertEquals("does-not-exist", response.getCharacterEncoding()); assertThrows(UnsupportedEncodingException.class, response::getWriter); response.reset(); response.setContentType("text/html"); response.setCharacterEncoding("ISO-8859-7"); assertEquals("text/html;charset=ISO-8859-7", response.getContentType()); } /** * Test setCharacterEncoding method. */ @SuppressWarnings("deprecation") @Test void testSetCharacterEncoding6() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setLocale(new Locale("ja")); assertEquals("shift_jis", response.getCharacterEncoding().toLowerCase()); } /** * Test setCommitted method. */ @Test void testSetCommitted() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); assertFalse(response.isCommitted()); response.setCommitted(true); assertTrue(response.isCommitted()); } /** * Test setContentLength method. */ @Test void testSetContentLength() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setContentLength(12); assertEquals("12", response.getHeader("Content-Length")); } /** * Test setContentLengthLong method. */ @Test void testSetContentLengthLong() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setContentLengthLong(12L); assertEquals("12", response.getHeader("Content-Length")); } /** * Test setContentType method. */ @Test void testSetContentType() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setContentType(null); assertNull(response.getContentType()); } /** * Test setContentType method. */ @Test void testSetContentType2() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setContentType("text/html"); assertEquals("text/html", response.getContentType()); assertEquals("ISO-8859-1", response.getCharacterEncoding()); } /** * Test setContentType method. */ @Test void testSetContentType3() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setContentType("text/html;charset=UTF-8"); assertEquals("text/html;charset=UTF-8", response.getContentType()); assertEquals("UTF-8", response.getCharacterEncoding()); } /** * Test setDateHeader method. */ @Test void testSetDateHeader() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setDateHeader("header", 1000); assertEquals("Thu, 1 Jan 1970 00:00:01 GMT", response.getHeader("header")); response.setDateHeader("header", 2000); assertEquals("Thu, 1 Jan 1970 00:00:02 GMT", response.getHeader("header")); } /** * Test setDateHeader method. */ @Test void testSetDateHeader2() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setDateHeader("header", 1000); assertEquals("Thu, 1 Jan 1970 00:00:01 GMT", response.getHeader("header")); } /** * Test setHeader method. */ @Test void testSetHeader() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setHeader("header", "value1"); response.addHeader("header", "value2"); response.setHeader("header", "value3"); assertEquals("value3", response.getHeader("header")); } /** * Test setHeader method. */ @Test void testSetHeader2() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setHeader("header", "value1"); response.setHeader("header", "value2"); assertEquals("value2", response.getHeader("header")); } /** * Test setHeader method. */ @Test void testSetHeader3() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setHeader("header", "value1"); assertEquals("value1", response.getHeader("header")); } /** * Test setIntHeader method. */ @Test void testSetIntHeader() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setIntHeader("header", 1); assertEquals("1", response.getHeader("header")); } /** * Test setLocale method. */ @Test void testSetLocale() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setLocale(GERMAN); assertEquals(GERMAN, response.getLocale()); } /** * Test setResponseCloser method. */ @Test void testSetResponseCloser() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setResponseCloser(() -> { }); assertNotNull(response.getResponseCloser()); } /** * Test setStatus method. */ @Test void testSetStatus() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setStatus(500); assertEquals(500, response.getStatus()); } /** * Test setTrailerFields method. */ @Test void testSetTrailerFields() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); assertThrows(IllegalStateException.class, () -> { response.setTrailerFields(() -> { return null; }); }); } /** * Test setWebApplication method. */ @Test void testSetWebApplication() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); assertEquals(webApplication, response.getWebApplication()); } /** * Test setWebApplicationOutputStream method. */ @Test void testSetWebAplicationOutputStream() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplicationOutputStream(new WebApplicationOutputStream() { @Override public void flushBuffer() { throw new UnsupportedOperationException("Not supported yet."); } @Override public int getBufferSize() { throw new UnsupportedOperationException("Not supported yet."); } @Override public OutputStream getOutputStream() { throw new UnsupportedOperationException("Not supported yet."); } @Override public WebApplicationResponse getResponse() { throw new UnsupportedOperationException("Not supported yet."); } @Override public WriteListener getWriteListener() { throw new UnsupportedOperationException("Not supported yet."); } @Override public void resetBuffer() { throw new UnsupportedOperationException("Not supported yet."); } @Override public void setBufferSize(int bufferSize) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void setOutputStream(OutputStream outputStream) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void setResponse(WebApplicationResponse response) { throw new UnsupportedOperationException("Not supported yet."); } @Override public boolean isReady() { throw new UnsupportedOperationException("Not supported yet."); } @Override public void setWriteListener(WriteListener writeListener) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void write(int b) { throw new UnsupportedOperationException("Not supported yet."); } }); assertNotNull(response.getWebApplicationOutputStream()); } /** * Test verifyNotCommitted method. */ @Test void testVerifyNotCommitted() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setCommitted(true); assertThrows(IllegalStateException.class, () -> { response.verifyNotCommitted("bogus"); }); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/DefaultWebApplicationTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationRequest; import cloud.piranha.core.api.WebApplicationResponse; import cloud.piranha.resource.impl.DefaultResourceManager; import cloud.piranha.resource.impl.DirectoryResource; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.FilterConfig; import jakarta.servlet.Servlet; import jakarta.servlet.ServletConfig; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletContextListener; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRegistration; import jakarta.servlet.ServletRegistration.Dynamic; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletRequestEvent; import jakarta.servlet.ServletRequestListener; import jakarta.servlet.ServletResponse; import jakarta.servlet.SessionTrackingMode; import static jakarta.servlet.SessionTrackingMode.COOKIE; import static jakarta.servlet.SessionTrackingMode.URL; import jakarta.servlet.descriptor.JspConfigDescriptor; import jakarta.servlet.descriptor.JspPropertyGroupDescriptor; import jakarta.servlet.descriptor.TaglibDescriptor; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.Date; import java.util.EnumSet; import java.util.Enumeration; import java.util.EventListener; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; /** * The JUnit tests for the DefaultWebApplication class. * * @author Manfred Riem (mriem@manorrock.com) */ class DefaultWebApplicationTest { /** * Test addFilter method. */ @Test void testAddFilter() { WebApplication webApplication = new DefaultWebApplication(); webApplication.addFilter("TestAddFilter", TestAddFilterFilter.class); assertNotNull(webApplication.getFilterRegistration("TestAddFilter")); } /** * Test addFilter method. */ @Test void testAddFilter2() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addFilter("TestAddFilter2", new Filter() { @Override public void init(FilterConfig filterConfig) throws ServletException { ServletContext servletContext = filterConfig.getServletContext(); servletContext.setAttribute("TestAddFilter2", true); throw new ServletException("TestAddFilter2"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { } }); webApplication.initialize(); assertNotNull(webApplication.getAttribute("TestAddFilter2")); } /** * Test addFilter method. * * @throws Exception when a serious error occurs. */ @Test void testAddFilter3() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addFilter("TestAddFilter3a", new Filter() { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setAttribute("TestAddFilter3a", "true"); chain.doFilter(request, response); } }); webApplication.addFilterMapping("TestAddFilter3a", "/*"); webApplication.addFilter("TestAddFilter3b", new Filter() { @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setAttribute("TestAddFilter3b", "true"); chain.doFilter(request, response); } @Override public void init(FilterConfig filterConfig) throws ServletException { } }); webApplication.addFilterMapping("TestAddFilter3b", "/*"); webApplication.addServlet("TestAddFilter3Servlet", new HttpServlet() { private static final long serialVersionUID = 1L; @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setStatus(200); } }); webApplication.addServletMapping("TestAddFilter3Servlet", "/testAddFilter3"); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.setServletPath("/testAddFilter3"); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.service(request, response); assertEquals(200, response.getStatus()); } /** * Test addFilter method. */ @Test void testAddFilter4() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initialize(); webApplication.start(); Filter filter = new Filter() { @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { } @Override public void init(FilterConfig filterConfig) throws ServletException { } }; assertThrows(IllegalStateException.class, () -> webApplication.addFilter("TestAddFilter4", filter)); } /** * Test addFilter method. */ @Test void testAddFilter5() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.addFilter("TestAddFilter5", TestAddFilter5Filter.class)); assertNotNull(webApplication.getFilterRegistration("TestAddFilter5")); assertEquals(TestAddFilter5Filter.class.getName(), webApplication.getFilterRegistration("TestAddFilter5").getClassName()); } /** * Test addFilter method. */ @Test void testAddFilter6() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.addFilter("TestAddFilter6", "doesnotexit")); } /** * Test addFilter method. */ @Test void testAddFilter7() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initialize(); webApplication.start(); assertThrows(IllegalStateException.class, () -> webApplication.addFilter("TestAddFilter7", "should throw IllegalStateException")); } /** * Test addFilter method. */ @Test void testAddFilter8() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initialize(); assertThrows(IllegalArgumentException.class, () -> webApplication.addFilter(null, "filter name is null so throw IllegalArgumentException")); } /** * Test addFilter method. */ @Test void testAddFilter9() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initialize(); assertThrows(IllegalArgumentException.class, () -> webApplication.addFilter(null, Filter.class)); } /** * Test addFilter method. */ @Test void testAddFilter10() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initialize(); assertNotNull(webApplication.addFilter("filter", Filter.class)); assertNull(webApplication.addFilter("filter", Filter.class)); } /** * Test addFilter method. */ @Test void testAddFilter11() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.addFilter("filter", "InCompleteRegistrationFilter")); assertNull(webApplication.addFilter("filter", "InCompleteRegistrationFilter")); } /** * Test addFilter method. */ @Test void testAddFilter12() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.addFilter("filter", "InCompleteRegistrationFilter")); assertNotNull(webApplication.getFilterRegistration("filter")); } /** * Test addFilter method. */ @Test void testAddFilter13() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.addFilter("filter", new Filter() { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { } })); assertNull(webApplication.addFilter("filter", new Filter() { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { } })); } /** * Test addFilterMapping method. * * @throws Exception when a serious error occurs. */ @Test void testAddFilterMapping() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addFilter("TestAddFilterMapping", new Filter() { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.getServletContext().setAttribute("TestAddFilterMapping", true); } }); webApplication.addFilterMapping("TestAddFilterMapping", "/*"); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.setServletPath("/testAddFilterMapping"); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.service(request, response); assertEquals(200, response.getStatus()); assertNotNull(webApplication.getAttribute("TestAddFilterMapping")); } /** * Test addInitializer method. */ @Test void testAddInitializer() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addInitializer((Set> classes, ServletContext servletContext) -> { servletContext.setAttribute("TestAddInitializer", true); }); webApplication.initialize(); assertNotNull(webApplication.getAttribute("TestAddInitializer")); } /** * Test addInitializer method. */ @Test void testAddInitializer2() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addInitializer(TestAddInitializer2Initializer.class.getName()); webApplication.initialize(); assertNotNull(webApplication.getAttribute("TestAddInitializer2")); } /** * Test addInitialized method */ @Test void testAddInitializer3() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addInitializer(TestAddInitializer3Initializer.class.getName()); webApplication.addInitializer(new ServletContainerInitializer() { @Override public void onStartup(Set> classes, ServletContext context) throws ServletException { WebApplication webApplication = (WebApplication) context; context.setAttribute("testAddInitializer3", webApplication.getInitializers().size()); } }); webApplication.initialize(); assertEquals(1, webApplication.getAttribute("testAddInitializer3")); } /** * Test addJspFile method. */ @Test void testAddJspFile() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNull(webApplication.addJspFile("TestAddJspFile", "testAddJspFile.jsp")); } /** * Test addJspFile method. */ @Test void testAddJspFile2() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initialize(); webApplication.start(); assertThrows(IllegalStateException.class, () -> webApplication.addJspFile("TestAddJspFile2", "testAddJspFile2.jsp")); } /** * Test addJspFile method. */ @Test void testAddJspFile3() { WebApplication webApplication = new DefaultWebApplication(); assertThrows(IllegalArgumentException.class, () -> webApplication.addJspFile(null, "testAddJspFile3.jsp")); } /** * Test addMimeType method. */ @Test void testAddMimeType() { WebApplication webApplication = new DefaultWebApplication(); assertNull(webApplication.getMimeType("my.class")); webApplication.addMimeType("class", "application/x-java-class"); assertEquals("application/x-java-class", webApplication.getMimeType("my.class")); } /** * Test addListener method. */ @Test void testAddListener() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initialize(); assertThrows(IllegalStateException.class, () -> webApplication.addListener("TestAddListener")); } /** * Test addListener method. */ @Test void testAddListener2() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertThrows(IllegalArgumentException.class, () -> webApplication.addListener(TestAddListener2Listener.class)); } /** * Test addListener method. * *

* Add a ServletRequestListener and validate the requestDestroyed method is * called when the request is processed. *

* * @throws Exception when a serious error occurs. */ @Test void testAddListener3() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addListener(new ServletRequestListener() { @Override public void requestDestroyed(ServletRequestEvent event) { event.getServletContext().setAttribute("testAddListener3", true); } }); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.service(request, response); assertNotNull(webApplication.getAttribute("testAddListener3")); } /** * Test addListener method. * *

* Add a ServletRequestListener and validate the requestInitialized method * is called when the request is processed. *

* * @throws Exception when a serious error occurs. */ @Test void testAddListener4() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addListener(new ServletRequestListener() { @Override public void requestInitialized(ServletRequestEvent event) { event.getServletContext().setAttribute("testAddListener4", true); } }); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.service(request, response); assertNotNull(webApplication.getAttribute("testAddListener4")); } /** * Test addListener method. * *

* Add a ServletContextListener and validate the contextDestroyed method is * called when the ServletContext gets destroyed. *

*/ @Test void testAddListener5() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addListener(new ServletContextListener() { @Override public void contextDestroyed(ServletContextEvent event) { event.getServletContext().setAttribute("testAddListener5", true); } }); webApplication.initialize(); webApplication.destroy(); assertNotNull(webApplication.getAttribute("testAddListener5")); } /** * Test addListener method. * *

* Add a ServletContextListener and validate the contextInitialized method * is called when the ServletContext gets initialized. *

*/ @Test void testAddListener6() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addListener(new ServletContextListener() { @Override public void contextInitialized(ServletContextEvent event) { event.getServletContext().setAttribute("testAddListener6", true); } }); webApplication.initialize(); webApplication.destroy(); assertNotNull(webApplication.getAttribute("testAddListener6")); } /** * Test addMapping method (verify the # of mappings > 0). */ @Test void testAddMapping() { DefaultWebApplication webApplication = new DefaultWebApplication(); ServletRegistration.Dynamic dynamic = webApplication.addServlet("echo", "servlet.EchoServlet"); assertNotNull(dynamic); dynamic.addMapping("/echo"); assertFalse(dynamic.getMappings().isEmpty()); } /** * Test addMapping method. * *

* Verify when we add twice addMapping will return a empty set. *

*/ @Test void testAddMapping2() { DefaultWebApplication webApplication = new DefaultWebApplication(); ServletRegistration.Dynamic dynamic = webApplication.addServlet("echo", "servlet.EchoServlet"); assertNotNull(dynamic); dynamic.addMapping("/echo"); assertFalse(dynamic.getMappings().isEmpty()); assertEquals(0, dynamic.addMapping("/echo").size()); } /** * Test addResource method. * * @throws Exception when a serious error occurs. */ @Test void testAddResource() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("."))); assertNotNull(webApplication.getResource("/src/main/java")); } /** * Test addServlet method. * * @throws Exception when a serious error occurs. */ @Test void testAddServlet() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addServlet("TestAddServletServlet", new HttpServlet() { @Override public void init(ServletConfig config) throws ServletException { config.getServletContext().setAttribute("TestAddServlet", true); throw new RuntimeException("TestAddServlet"); } }); webApplication.addServletMapping("TestAddServletServlet", "/testAddServlet"); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setServletPath("/testAddServlet"); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); try { webApplication.service(request, response); fail(); } catch (RuntimeException ue) { } assertNotNull(webApplication.getAttribute("TestAddServlet")); } /** * Test addServlet method. * * @throws Exception when a serious error occurs. */ @Test void testAddServlet2() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.addServlet("TestAddServlet2Servlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().println("TestAddServlet2Servlet"); } })); webApplication.addServletMapping("TestAddServlet2Servlet", "/testAddServlet2"); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setServletPath("/testAddServlet2"); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.service(request, response); assertEquals(200, response.getStatus()); } /** * Test addServlet method. */ @Test void testAddServlet3() { DefaultWebApplication webApplication = new DefaultWebApplication(); ServletRegistration.Dynamic dynamic = webApplication.addServlet( "TestAddServlet3Servlet", Servlet.class); assertNotNull(dynamic); } /** * Test addServlet method. */ @Test void testAddServlet4() { DefaultWebApplication webApplication = new DefaultWebApplication(); ServletRegistration.Dynamic dynamic = webApplication.addServlet( "TestAddServlet4Servlet", "TestAddServlet4Servlet"); assertNotNull(dynamic); } /** * Test addServletMapping test. * * @throws Exception when a serious error occurs. */ @Test void testAddServletMapping() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.addServlet("TestAddServletMappingServlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().println("TestAddServletMappingServlet"); } })); webApplication.addServletMapping("TestAddServletMappingServlet", "/testAddServletMappingServlet"); WebApplicationRequest request = new DefaultWebApplicationRequest(); request.setServletPath("/testAddServletMappingServlet"); request.setWebApplication(webApplication); WebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.initialize(); webApplication.start(); webApplication.service(request, response); assertEquals(200, response.getStatus()); } /** * Test createFilter method. */ @Test void testCreateFilter() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertThrows(ServletException.class, () -> webApplication.createFilter(Filter.class)); } /** * Test createListener method. */ @Test void testCreateListener() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertThrows(IllegalArgumentException.class, () -> webApplication.createListener(TestCreateListenerEventListener.class)); } /** * Test createListener method. */ @Test void testCreateListener2() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.createListener( TestCreateListener2ServletContextListener.class)); } /** * Test createListener method. * * @throws Exception when a serious error occurs. */ @Test void testCreateListener3() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.createListener( TestCreateListener3ServletRequestListener.class)); } /** * Test createServlet method. */ @Test void testCreateServlet() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertThrows(ServletException.class, () -> webApplication.createServlet(Servlet.class)); } /** * Test declareRoles method. */ @Test void testDeclareRoles() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.declareRoles("testDeclareRoles"); } /** * Test destroy method. * * @throws Exception when a serious error occurs. */ @Test void testDestroy() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addListener(new ServletContextListener() { @Override public void contextDestroyed(ServletContextEvent event) { event.getServletContext().setAttribute("testDestroy", true); } }); webApplication.initialize(); webApplication.destroy(); assertNotNull(webApplication.getAttribute("testDestroy")); } /** * Test getAsync. * * REVIEW LOCATION * * @throws Exception */ @Test void testGetAsync() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); Dynamic registration = webApplication.addServlet("Chat", TestChat1Servlet.class); registration.setAsyncSupported(true); webApplication.addServletMapping("Chat", "/chat"); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setServletPath("/chat"); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); response.setWebApplication(webApplication); response.getWebApplicationOutputStream().setOutputStream(byteOutput); webApplication.service(request, response); assertTrue(byteOutput.toByteArray().length > 0); request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.setAsyncSupported(true); request.setServletPath("/chat"); request.setMethod("POST"); request.setParameter("action", new String[]{"login"}); request.setParameter("name", new String[]{"username"}); response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); byteOutput = new ByteArrayOutputStream(); response.getWebApplicationOutputStream().setOutputStream(byteOutput); webApplication.service(request, response); assertTrue(byteOutput.toByteArray().length > 0); request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.setServletPath("/chat"); request.setMethod("POST"); request.setParameter("action", new String[]{"post"}); request.setParameter("name", new String[]{"username"}); request.setParameter("message", new String[]{new Date().toString()}); response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); byteOutput = new ByteArrayOutputStream(); response.getWebApplicationOutputStream().setOutputStream(byteOutput); webApplication.service(request, response); assertTrue(byteOutput.toByteArray().length > 0); } /** * Test getAsync. * * REVIEW LOCATION * * @throws Exception */ @Test void testGetAsync2() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); Dynamic registration = webApplication.addServlet("Chat", TestChat2Servlet.class); registration.setAsyncSupported(true); webApplication.addServletMapping("Chat", "/chat"); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.setServletPath("/chat"); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); response.getWebApplicationOutputStream().setOutputStream(byteOutput); webApplication.service(request, response); assertTrue(byteOutput.toByteArray().length > 0); request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.setServletPath("/chat"); request.setMethod("POST"); request.setParameter("action", new String[]{"login"}); request.setParameter("name", new String[]{"username"}); response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); byteOutput = new ByteArrayOutputStream(); response.getWebApplicationOutputStream().setOutputStream(byteOutput); webApplication.service(request, response); assertTrue(byteOutput.toByteArray().length > 0); request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.setServletPath("/chat"); request.setMethod("POST"); request.setParameter("action", new String[]{"post"}); request.setParameter("name", new String[]{"username"}); request.setParameter("message", new String[]{new Date().toString()}); response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); byteOutput = new ByteArrayOutputStream(); response.getWebApplicationOutputStream().setOutputStream(byteOutput); webApplication.service(request, response); assertTrue(byteOutput.toByteArray().length > 0); } /** * Test getAttribute method. */ @Test void testGetAttribute() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertThrows(NullPointerException.class, () -> webApplication.getAttribute(null)); } /** * Test getAttributeNames method. */ @Test void testGetAttributeNames() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.getAttributeNames()); } /** * Test getContext method. */ @Test void testGetContext() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNull(webApplication.getContext("/context")); } /** * Test getContextPath method. */ @Test void testGetContextPath() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertEquals("", webApplication.getContextPath()); } /** * Test getDefaultServlet method. */ @Test void testGetDefaultServlet() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNull(webApplication.getDefaultServlet()); } /** * Test getDefaultSessionTrackingModes method. */ @Test void testGetDefaultSessionTrackingModes() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertTrue(webApplication.getDefaultSessionTrackingModes().contains(COOKIE)); } /** * Test getEffectiveMajorVersion method. */ @Test void testGetEffectiveMajorVersion() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertEquals(6, webApplication.getEffectiveMajorVersion()); } /** * Test getEffectiveMinorVersion method. */ @Test void testGetEffectiveMinorVersion() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertEquals(0, webApplication.getEffectiveMinorVersion()); } /** * Test getEffectiveSessionTrackingMode method. */ @Test void testGetEffectiveSessionTrackingMode() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertTrue(webApplication.getEffectiveSessionTrackingModes().contains(COOKIE)); } /** * Test getEffectiveSessionTrackingModes method. */ @Test void testGetEffectiveSessionTrackingModes2() { DefaultWebApplication webApplication = new DefaultWebApplication(); Set trackingModes = EnumSet.of(URL); webApplication.setSessionTrackingModes(trackingModes); assertTrue(webApplication.getEffectiveSessionTrackingModes().contains(URL)); } /** * Test getFilterRegistration method. */ @Test void testGetFilterRegistration() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addFilter("TestGetFilterRegistrationFilter", new Filter() { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { } }); assertNotNull(webApplication.getFilterRegistration("TestGetFilterRegistrationFilter")); } /** * Test getFilterRegistration method. */ @Test void testGetFilterRegistration2() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addFilter("testGetFilterRegistration2Filter", "TestGetFilterRegistration2Filter"); assertNotNull(webApplication.getFilterRegistration("testGetFilterRegistration2Filter")); } /** * Test getFilterRegistrations method. */ @Test void testGetFilterRegistrations() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addFilter("testGetFilterRegistrationsFilter", "testGetFilterRegistrationsFilter"); assertFalse(webApplication.getFilterRegistrations().isEmpty()); } /** * Test getInitParameter method. */ @Test void testGetInitParameter() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setInitParameter("testGetInitParameter", Boolean.TRUE.toString()); assertEquals("true", webApplication.getInitParameter("testGetInitParameter")); } /** * Test getInitParameterNames method. */ @Test void testGetInitParameterNames() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setInitParameter("testGetInitParameterNames", Boolean.TRUE.toString()); Enumeration enumeration = webApplication.getInitParameterNames(); assertEquals("testGetInitParameterNames", enumeration.nextElement()); assertFalse(enumeration.hasMoreElements()); } /** * Test getInitializers method. */ @Test void testGetInitializers() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.getInitializers()); assertTrue(webApplication.getInitializers().isEmpty()); } /** * Test getJspConfigDescriptor method. * * @jaarta.assertion Servlet:JAVADOC:690 */ @Test void testGetJspConfigDescriptor() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNull(webApplication.getJspConfigDescriptor()); } /** * Test getMajorVersion method. */ @Test void testGetMajorVersion() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertEquals(6, webApplication.getMajorVersion()); } /** * Test getManager method. */ @Test void testGetManager() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.getManager()); } /** * Test getMappings method. */ @Test void testGetMappings() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.getMappings("TestGetMappingsServlet")); assertTrue(webApplication.getMappings("TestGetMappingsServlet").isEmpty()); } /** * Test getMimeType method. */ @Test void testGetMimeType() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.getMimeType("index.html")); assertEquals("text/html", webApplication.getMimeType("index.html")); } /** * Test getMimeType method. */ @Test void testGetMimeType2() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertEquals("text/css", webApplication.getMimeType("TEST.CSS")); assertEquals("text/javascript", webApplication.getMimeType("TEST.JS")); } /** * Test getMimeType method. */ @Test void testGetMimeType3() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNull(webApplication.getMimeType("this_maps_to.null")); } /** * Test getMimeType method. */ @Test void testGetMimeType4() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addMimeType("class", "application/x-java-class"); assertEquals(webApplication.getMimeType("my.class"), "application/x-java-class"); } /** * Test getMimeType method. */ @Test void testGetMimeType5() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNull(webApplication.getMimeType("myclass")); } /** * Test getMinorVersion method. */ @Test void testGetMinorVersion() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertEquals(0, webApplication.getMinorVersion()); } /** * Test getRealPath method. */ @Test void testGetRealPath() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNull(webApplication.getRealPath("index.html")); } /** * Test getRealPath method. */ @Test void testGetRealPath2() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("."))); assertNotNull(webApplication.getRealPath("/src/main/java")); } /** * Test getRequest method. */ @Test void testGetRequest() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); webApplication.linkRequestAndResponse(request, response); assertNotNull(webApplication.getRequest(response)); } /** * Test getRequestCharacterEncoding method. */ @Test void testGetRequestCharacterEncoding() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNull(webApplication.getRequestCharacterEncoding()); webApplication.setRequestCharacterEncoding("UTF-8"); assertEquals("UTF-8", webApplication.getRequestCharacterEncoding()); } /** * Test getResource method. * * @throws Exception when a serious error occurs. */ @Test void testGetResource() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNull(webApplication.getResource("/testGetResource")); } /** * Test getResourceAsStream method. */ @Test void testGetResourceAsStream() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("."))); assertNotNull(webApplication.getResourceAsStream("/pom.xml")); } /** * Test getResourcePaths method. */ @Test void testGetResourcePaths() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNull(webApplication.getResourcePaths("/testGetResourcePaths/")); assertNull(webApplication.getResourcePaths(null)); } /** * Test getResourcePaths method. * *

* Simulating the Javadoc example of the getResourcePaths method. *

*/ @Test void testGetResourcePaths2() { DefaultResourceManager resourceManager = new DefaultResourceManager(); DefaultWebApplication webApp = new DefaultWebApplication(); webApp.getManager().setResourceManager(resourceManager); webApp.addResource(new DirectoryResource("src/test/webapp/resourcepaths")); Set resourcePathsRoot = webApp.getResourcePaths("/"); assertNotNull(resourcePathsRoot); assertTrue(resourcePathsRoot.contains("/catalog/")); assertTrue(resourcePathsRoot.contains("/customer/")); assertTrue(resourcePathsRoot.contains("/welcome.html")); Set resourcePathsCatalog = webApp.getResourcePaths("/catalog/"); assertNotNull(resourcePathsCatalog); assertTrue(resourcePathsCatalog.contains("/catalog/offers/")); assertTrue(resourcePathsCatalog.contains("/catalog/products.html")); assertTrue(resourcePathsCatalog.contains("/catalog/index.html")); Set resourcePathsCatalogOffers = webApp.getResourcePaths("/catalog/offers"); assertNotNull(resourcePathsCatalogOffers); assertTrue(resourcePathsCatalogOffers.contains("/catalog/offers/books.html")); assertTrue(resourcePathsCatalogOffers.contains("/catalog/offers/music.html")); } /** * Test getResourcePaths method. */ @Test void testGetResourcePaths3() { DefaultWebApplication webApp = new DefaultWebApplication(); assertThrows(IllegalArgumentException.class, () -> webApp.getResourcePaths("")); } /** * Test getResourcePaths method. */ @Test void testGetResourcePaths4() { DefaultResourceManager resourceManager = new DefaultResourceManager(); DefaultWebApplication webApp = new DefaultWebApplication(); webApp.getManager().setResourceManager(resourceManager); webApp.addResource(new DirectoryResource("src/test/webapp/resourcepaths")); assertNull(webApp.getResourcePaths("/welcome.html")); assertNull(webApp.getResourcePaths("/catalog/products.html")); assertNull(webApp.getResourcePaths("/catalog/offers/books.html")); } /** * Test getResponse method. */ @Test void testGetResponse() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); webApplication.linkRequestAndResponse(request, response); assertNotNull(webApplication.getResponse(request)); } /** * Test getResponseCharacterEncoding. */ @Test void testGetResponseCharacterEncoding() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNull(webApplication.getResponseCharacterEncoding()); webApplication.setResponseCharacterEncoding("UTF-8"); assertEquals("UTF-8", webApplication.getResponseCharacterEncoding()); } /** * Test getSecurityManager. * * REVIEW LOCATION. */ @Test void testGetSecurityManager() { DefaultWebApplication webApp = new DefaultWebApplication(); assertNotNull(webApp.getManager().getSecurityManager()); } /** * Test getServerInfo method. */ @Test void testGetServerInfo() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.getServerInfo()); assertFalse(webApplication.getServerInfo().trim().equals("")); } /** * Test getServletContextId method. */ @Test void testGetServletContextId() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.getServletContextId()); } /** * Test getServletContextName method. */ @Test void testGetServletContextName() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.getServletContextName()); } /** * Test getServletRegistration method. */ @Test void testGetServletRegistration() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addServlet("TestGetServletRegistrationServlet", "TestGetServletRegistrationServlet"); assertNotNull(webApplication.getServletRegistration("TestGetServletRegistrationServlet")); } /** * Test getServletRegistrations method. */ @Test void testGetServletRegistration2() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addServlet("TestGetServletRegistration2Servlet", TestGetServletRegistration2Servlet.class); assertNotNull(webApplication.getServletRegistration("TestGetServletRegistration2Servlet")); } /** * Test getServletRegistrations method. */ @Test void testGetServletRegistrations() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addServlet("servlet", TestGetServletRegistrationsServlet.class); assertFalse(webApplication.getServletRegistrations().isEmpty()); } /** * Test getServletRegistrations method. */ @Test void testGetServletRegistrations2() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.getServletRegistrations()); assertTrue(webApplication.getServletRegistrations().isEmpty()); } /** * Test getVirtualServerName method. */ @Test void testGetVirtualServerName() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertEquals("server", webApplication.getVirtualServerName()); } /** * Test include. * * REVIEW LOCATION * * @throws Exception when a serious error occurred. */ @Test void testInclude2() throws Exception { DefaultWebApplication webApp = new DefaultWebApplication(); webApp.addServlet("Include", TestIncludeServlet.class); webApp.addServletMapping("Include", "/include"); webApp.addServlet("Include2", TestInclude2Servlet.class); webApp.addServletMapping("Include2", "/include2"); webApp.addServlet("Include", TestIncludeServlet.class); webApp.addServletMapping("Include", "/include"); webApp.addServlet("Include3", TestInclude3Servlet.class); webApp.addServletMapping("Include3", "/include3"); webApp.initialize(); webApp.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApp); request.setServletPath("/include3"); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); response.getWebApplicationOutputStream().setOutputStream(byteOutput); response.setWebApplication(webApp); webApp.service(request, response); assertTrue(new String(byteOutput.toByteArray()).contains("This was included")); } /** * Test include. * * REVIEW LOCATION * * @throws Exception when a serious error occurred. */ @Test void testInclude3() throws Exception { DefaultWebApplication webApp = new DefaultWebApplication(); webApp.addServlet("Include", TestIncludeServlet.class); webApp.addServletMapping("Include", "/include"); webApp.addServlet("Include2", TestInclude2Servlet.class); webApp.addServletMapping("Include2", "/include2"); webApp.addServlet("Include", TestIncludeServlet.class); webApp.addServletMapping("Include", "/include"); webApp.addServlet("Include3", TestInclude3Servlet.class); webApp.addServletMapping("Include3", "/include3"); webApp.addServlet("Include4", TestInclude4Servlet.class); webApp.addServletMapping("Include4", "/include4"); webApp.initialize(); webApp.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApp); request.setServletPath("/include4"); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); response.getWebApplicationOutputStream().setOutputStream(byteOutput); response.setWebApplication(webApp); webApp.service(request, response); assertTrue(new String(byteOutput.toByteArray()).contains("This was includedThis was included")); } /** * Test initialize method. */ @Test void testInitialize() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initialize(); assertTrue(webApplication.isInitialized()); } /** * Test initializeFilters method. */ @Test void testInitializeFilters() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initializeFilters(); assertFalse(webApplication.isInitialized()); } /** * Test initializeFinish method. */ @Test void testInitializeFinish() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initializeFinish(); assertTrue(webApplication.isInitialized()); } /** * Test initializeInitializers method. */ @Test void testInitializeInitializers() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initializeInitializers(); assertFalse(webApplication.isInitialized()); } /** * Test initializeServlets method. */ @Test void testInitializeServlets() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initializeServlets(); assertFalse(webApplication.isInitialized()); } /** * Test isDistributable method. */ @Test void testIsDistributable() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertFalse(webApplication.isDistributable()); } /** * Test isInitialized method. */ @Test void testIsInitialized() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertFalse(webApplication.isInitialized()); webApplication.initialize(); assertTrue(webApplication.isInitialized()); } /** * Test isMetadataComplete method. */ @Test void testIsMetadataComplete() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertFalse(webApplication.isMetadataComplete()); } /** * Test linkRequestAndResponse method. */ @Test void testLinkRequestAndResponse() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); assertNull(webApplication.getRequest(response)); assertNull(webApplication.getResponse(request)); webApplication.linkRequestAndResponse(request, response); assertEquals(response, webApplication.getResponse(request)); assertEquals(request, webApplication.getRequest(response)); } /** * Test log method. */ @Test void testLog() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.log("TestLog"); } /** * Test log method. */ @Test void testLog2() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.log("TestLog", new RuntimeException()); } /** * Test service method. * * @throws Exception when a serious error occurs. */ @Test void testService() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.addServlet("TestServiceServlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setStatus(403); } })); webApplication.addServletMapping("TestServiceServlet", "/testService"); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setServletPath("/testService"); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.service(request, response); assertEquals(403, response.getStatus()); } /** * Test service method (ServletRequestListeners) * * @throws Exception */ @Test void testService2() throws Exception { DefaultWebApplicationRequestMapper webAppRequestMapper = new DefaultWebApplicationRequestMapper(); DefaultWebApplication webApp = new DefaultWebApplication(); webApp.setWebApplicationRequestMapper(webAppRequestMapper); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApp); request.setServletPath("/Snoop"); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); response.getWebApplicationOutputStream().setOutputStream(byteOutput); response.setWebApplication(webApp); webApp.addServletMapping("Snoop", "/Snoop"); webApp.addServlet("DefaultServlet", DefaultServlet.class.getName()); webApp.addServletMapping("DefaultServlet", "/*"); webApp.initialize(); webApp.start(); webApp.service(request, response); assertEquals(404, response.getStatus()); } /** * Test service method. * * @throws Exception when a serious error occurs. */ @Test void testService3() throws Exception { DefaultWebApplication webApp = new DefaultWebApplication(); webApp.setWebApplicationRequestMapper(new DefaultWebApplicationRequestMapper()); webApp.addServletMapping("Snoop", "/Snoop"); webApp.addServlet("DefaultServlet", DefaultServlet.class.getName()); webApp.addServletMapping("DefaultServlet", "/*"); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApp); request.setServletPath("/Snoop"); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); response.getWebApplicationOutputStream().setOutputStream(byteOutput); response.setWebApplication(webApp); webApp.initialize(); webApp.start(); webApp.service(request, response); assertEquals(404, response.getStatus()); } /** * Test setAttribute method. */ @Test void testSetAttribute() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setAttribute("testSetAttribute", true); assertTrue((boolean) webApplication.getAttribute("testSetAttribute")); } /** * Test setAttribute method. */ @Test void testSetAttribute2() { DefaultWebApplication webApp = new DefaultWebApplication(); assertThrows(NullPointerException.class, () -> webApp.setAttribute(null, "KABOOM")); } /** * Test setClassLoader method. */ @Test void testSetClassLoader() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.getClassLoader()); webApplication.setClassLoader(null); assertNull(webApplication.getClassLoader()); } /** * Test setContextPath method. */ @Test void testSetContextPath() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setContextPath("/setContextPath"); assertEquals("/setContextPath", webApplication.getContextPath()); } /** * Test setDefaultServlet method. */ @Test void testSetDefaultServlet() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNull(webApplication.getDefaultServlet()); webApplication.setDefaultServlet(new HttpServlet() { }); assertNotNull(webApplication.getDefaultServlet()); } /** * Test setDistributable method. */ @Test void testSetDistributable() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertFalse(webApplication.isDistributable()); webApplication.setDistributable(true); assertTrue(webApplication.isDistributable()); } /** * Test setEffectiveMajorVersion method. */ @Test void testSetEffectiveMajorVersion() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertEquals(6, webApplication.getEffectiveMajorVersion()); webApplication.setEffectiveMajorVersion(5); assertEquals(5, webApplication.getEffectiveMajorVersion()); } /** * Test setEffectiveMinoVersion method. */ @Test void testSetEffectiveMinorVersion() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertEquals(0, webApplication.getEffectiveMinorVersion()); webApplication.setEffectiveMinorVersion(5); assertEquals(5, webApplication.getEffectiveMinorVersion()); } /** * Test setEffectiveSessionTrackingModes method. */ @Test void testSetEffectiveSessionTrackingModes() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initialize(); webApplication.start(); Set trackingModes = EnumSet.of(URL); assertNotNull(assertThrows(IllegalStateException.class, () -> webApplication.setSessionTrackingModes(trackingModes))); } /** * Test setInitParameter method. */ @Test void testSetInitParameter() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertTrue(webApplication.setInitParameter("name", "value")); assertFalse(webApplication.setInitParameter("name", "value")); } /** * Test setInitParameter method. */ @Test void testSetInitParameter2() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initialize(); webApplication.start(); assertThrows(IllegalStateException.class, () -> webApplication.setInitParameter("name", "value")); } /** * Test setInitParameter method. */ @Test void testSetInitParameter3() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertThrows(NullPointerException.class, () -> webApplication.setInitParameter(null, "TestSetInitParameter3")); } /** * Test setInitParameter method. */ @Test void testSetInitParameter4() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initialize(); webApplication.start(); assertThrows(NullPointerException.class, () -> webApplication.setInitParameter(null, "TestSetInitParameter4")); } /** * Test setJspConfigDescriptor method. */ @Test void testSetJspConfigDescriptor() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNull(webApplication.getJspConfigDescriptor()); webApplication.setJspConfigDescriptor(new JspConfigDescriptor() { @Override public Collection getJspPropertyGroups() { return null; } @Override public Collection getTaglibs() { return null; } }); assertNotNull(webApplication.getJspConfigDescriptor()); } /** * Test setMetadataComplete method. */ @Test void testSetMetadataComplete() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertFalse(webApplication.isMetadataComplete()); webApplication.setMetadataComplete(true); assertTrue(webApplication.isMetadataComplete()); } /** * Test setRequestCharacterEncoding method. */ @Test void testSetRequestCharacterEncoding() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNull(webApplication.getRequestCharacterEncoding()); webApplication.setRequestCharacterEncoding("UTF-8"); assertEquals("UTF-8", webApplication.getRequestCharacterEncoding()); } /** * Test setResponseCharacterEncoding. */ @Test void testSetResponseCharacterEncoding() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNull(webApplication.getResponseCharacterEncoding()); webApplication.setResponseCharacterEncoding("UTF-8"); assertEquals("UTF-8", webApplication.getResponseCharacterEncoding()); } /** * Test setServletContextName method. */ @Test void testSetServletContextName() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setServletContextName("TestSetServletContextName"); assertNotNull(webApplication.getServletContextName()); assertEquals("TestSetServletContextName", webApplication.getServletContextName()); } /** * Test setVirtualServerName method. */ @Test void testSetVirtualServerName() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setVirtualServerName("myname"); assertEquals("myname", webApplication.getVirtualServerName()); } /** * Test setWebApplicationRequestMapper method. * * @throws Exception when a serious error occurs. */ @Test void testSetWebApplicationRequestMapper() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setWebApplicationRequestMapper(null); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); webApplication.initialize(); webApplication.start(); assertThrows(NullPointerException.class, () -> webApplication.service(request, response)); } /** * Test start method. */ @Test void testStart() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initialize(); webApplication.start(); assertTrue(webApplication.isServicing()); } /** * Test start method. */ @Test void testStart2() { DefaultWebApplication webApp = new DefaultWebApplication(); webApp.initialize(); webApp.start(); webApp.stop(); webApp.destroy(); try { webApp.start(); fail(); } catch (RuntimeException e) { } } /** * Test stop method. */ @Test void testStop() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initialize(); webApplication.start(); assertTrue(webApplication.isServicing()); webApplication.stop(); assertFalse(webApplication.isServicing()); } /** * Test removeAttribute method. */ @Test void testRemoveAttribute() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setAttribute("name", "value"); assertNotNull(webApplication.getAttribute("name")); webApplication.removeAttribute("name"); assertNull(webApplication.getAttribute("name")); } /** * Test removeServletMapping method. */ @Test void testRemoveServletMapping() { DefaultWebApplication webApplication = new DefaultWebApplication(); assertNotNull(webApplication.addServlet("TestRemoveServletMappingServlet", new HttpServlet() { })); webApplication.addServletMapping("TestRemoveServletMappingServlet", "/testRemoveServletMapping"); assertEquals("TestRemoveServletMappingServlet", webApplication .removeServletMapping("/testRemoveServletMapping")); } /** * Test unlinkRequestAndResponse method. */ @Test void testUnlinkRequestAndResponse() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); webApplication.linkRequestAndResponse(request, response); assertEquals(response, webApplication.getResponse(request)); assertEquals(request, webApplication.getRequest(response)); webApplication.unlinkRequestAndResponse(request, response); assertNull(webApplication.getRequest(response)); assertNull(webApplication.getResponse(request)); } /** * Test filter that is used by testAddFilter. */ class TestAddFilterFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { } } /** * Test filter that is used by testAddFilter5. */ class TestAddFilter5Filter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request, response); } } /** * Test initializer that is used by testAddInitializer2. */ public static class TestAddInitializer2Initializer implements ServletContainerInitializer { @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { servletContext.setAttribute("TestAddInitializer2", true); } } /** * Test initializer that is used by testAddInitializer2. */ public static class TestAddInitializer3Initializer implements ServletContainerInitializer { /** * Constructor. * * @throws IOException when an I/O error occurs. */ public TestAddInitializer3Initializer() throws IOException { throw new IOException(); } @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { } } /** * Test listener that is used by testAddListener2. */ public static class TestAddListener2Listener implements EventListener { } /** * Test listener that is used by testCreateListener. */ public static class TestCreateListenerEventListener implements EventListener { } /** * Test listener that is used by testCreateListener2. */ public static class TestCreateListener2ServletContextListener implements ServletContextListener { } /** * Test listener that is used by testCreateListener3. */ public static class TestCreateListener3ServletRequestListener implements ServletRequestListener { } /** * Test servlet that is used by testGetServletRegistration2. */ public static class TestGetServletRegistration2Servlet extends HttpServlet { } /** * Test servlet that is used by testGetServletRegistrations. */ public static class TestGetServletRegistrationsServlet extends HttpServlet { } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/DefaultWebConnectionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; /** * The JUnit tests for the DefaultWebConnection class. * * @author Manfred Riem (mriem@manorrock.com) */ class DefaultWebConnectionTest { /** * Test close method. */ @Test void testClose() throws Exception { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); DefaultWebConnection connection = new DefaultWebConnection(request, response); connection.close(); assertFalse(connection.getInputStream().isReady()); assertFalse(connection.getOutputStream().isReady()); } /** * Test getInputStream method. */ @Test void testGetInputStream() throws Exception { DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); @SuppressWarnings("resource") DefaultWebConnection connection = new DefaultWebConnection(request, null); assertNotNull(connection.getInputStream()); } /** * Test getOutputStream method. */ @Test void testGetOutputStream() throws Exception { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); @SuppressWarnings("resource") DefaultWebConnection connection = new DefaultWebConnection(null, response); assertNotNull(connection.getOutputStream()); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/FilterTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.FilterConfig; import jakarta.servlet.FilterRegistration; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import cloud.piranha.core.api.WebApplication; /** * The JUnit tests for testing everything related to the Filter API. * * @author Manfred Riem (mriem@manorrock.com) */ class FilterTest { /** * Stores the web application. */ protected WebApplication webApp; /** * Setup before testing. * * @throws Exception when a serious error occurs. */ @BeforeEach void setUp() throws Exception { webApp = new DefaultWebApplication(); } /** * Test doFilter method. * * @throws Exception when a serious error occurs. */ @Test void testDoFilter() throws Exception { webApp.addFilter("Filter 1", new TestMultiple1Filter()); webApp.addFilterMapping("Filter 1", "/*"); webApp.addServlet("End Servlet", new TestEndServlet()); webApp.addServletMapping("End Servlet", "/doFilter"); webApp.initialize(); webApp.start(); TestWebApplicationRequest request = new TestWebApplicationRequest(); request.setWebApplication(webApp); request.setServletPath("/doFilter"); TestWebApplicationResponse response = new TestWebApplicationResponse(); response.setWebApplication(webApp); webApp.service(request, response); assertEquals(200, response.getStatus()); } @Test void testAddFilterGetClassName() throws Exception{ Filter filter = new TestMultiple1Filter(); FilterRegistration.Dynamic registration = webApp.addFilter("filter", filter); assertEquals(TestMultiple1Filter.class.getName(), registration.getClassName()); } /** * Test filter used in a test with multiple filters. */ class TestMultiple1Filter implements Filter { /** * Destroy the filter. */ @Override public void destroy() { } /** * Process the filter. * * @param request the request. * @param response the response. * @param chain the chain. * @throws IOException when an I/O error occurs. * @throws ServletException when a servlet error occurs. */ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setAttribute("TestMultiple1Filter", "true"); chain.doFilter(request, response); } /** * Initialize the filter. * * @param filterConfig the filter configuration. * @throws ServletException when a servlet error occurs. */ @Override public void init(FilterConfig filterConfig) throws ServletException { } } /** * Test filter used in a test with multiple filters. */ class TestMultiple2Filter implements Filter { /** * Destroy the filter. */ @Override public void destroy() { } /** * Process the filter. * * @param request the request. * @param response the response. * @param chain the chain. * @throws IOException when an I/O error occurs. * @throws ServletException when a servlet error occurs. */ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setAttribute("TestMultiple2Filter", "true"); chain.doFilter(request, response); } /** * Initialize the filter. * * @param filterConfig the filter configuration. * @throws ServletException when a servlet error occurs. */ @Override public void init(FilterConfig filterConfig) throws ServletException { } } /** * Test servlet to end a filter chain with a 200 response code. */ class TestEndServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * Handles the GET request. * * @param request the servlet request. * @param response the servlet response. * @throws IOException when an I/O error occurs * @throws ServletException when a servlet error occurs */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setStatus(200); } } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/HttpSessionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplication; import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletContextListener; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRegistration; import jakarta.servlet.SessionTrackingMode; import static jakarta.servlet.SessionTrackingMode.COOKIE; import static jakarta.servlet.SessionTrackingMode.SSL; import static jakarta.servlet.SessionTrackingMode.URL; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpSessionAttributeListener; import jakarta.servlet.http.HttpSessionBindingEvent; import jakarta.servlet.http.HttpSessionBindingListener; import jakarta.servlet.http.HttpSessionEvent; import jakarta.servlet.http.HttpSessionIdListener; import jakarta.servlet.http.HttpSessionListener; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.EnumSet; import java.util.HashSet; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; /** * The JUnit tests for the HttpSession related APIs. * * @author Manfred Riem (mriem@manorrock.com) */ public class HttpSessionTest { /** * Test attributeAdded method. * * @throws Exception when a serious error occurs. */ @Test void testAttributeAdded() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("TestAttributeAddedServlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getSession().setAttribute("name", "value"); } }) .servletMapping("TestAttributeAddedServlet", "/testAttributeAdded") .build(); webApplication.addListener(new HttpSessionAttributeListener() { @Override public void attributeAdded(HttpSessionBindingEvent event) { event.getSession().getServletContext() .setAttribute("testAttributeAdded", true); } }); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .servletPath("/testAttributeAdded") .webApplication(webApplication) .build(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .build(); webApplication.service(request, response); assertNotNull(webApplication.getAttribute("testAttributeAdded")); } /** * Test attributeRemoved method. * * @throws Exception when a serious error occurs. */ @Test void testAttributeRemoved() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("TestAttributeRemovedServlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getSession().setAttribute("name", "value"); request.getSession().removeAttribute("name"); } }) .servletMapping("TestAttributeRemovedServlet", "/testAttributeRemoved") .build(); webApplication.addListener(new HttpSessionAttributeListener() { @Override public void attributeRemoved(HttpSessionBindingEvent event) { event.getSession().getServletContext() .setAttribute("testAttributeRemoved", true); } }); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .servletPath("/testAttributeRemoved") .webApplication(webApplication) .build(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .build(); webApplication.service(request, response); assertNotNull(webApplication.getAttribute("testAttributeRemoved")); } /** * Test attributeReplaced method. * * @throws Exception when a serious error occurs. */ @Test void testAttributeReplaced() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("TestAttributeReplacedServlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getSession().setAttribute("name", "value"); request.getSession().setAttribute("name", "value2"); } }) .servletMapping("TestAttributeReplacedServlet", "/testAttributeReplaced") .build(); webApplication.addListener(new HttpSessionAttributeListener() { @Override public void attributeReplaced(HttpSessionBindingEvent event) { event.getSession().getServletContext() .setAttribute("testAttributeReplaced", true); } }); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .servletPath("/testAttributeReplaced") .webApplication(webApplication) .build(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .build(); webApplication.service(request, response); assertNotNull(webApplication.getAttribute("testAttributeReplaced")); } /** * Test sessionCreated method. * * @throws Exception when a serious error occurs. */ @Test void testSessionCreated() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("TestSessionCreatedServlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getSession(); } }) .servletMapping("TestSessionCreatedServlet", "/testSessionCreatedServlet") .build(); webApplication.addListener(new HttpSessionListener() { @Override public void sessionCreated(HttpSessionEvent event) { event.getSession().getServletContext().setAttribute("testSessionCreated", true); } }); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .servletPath("/testSessionCreatedServlet") .webApplication(webApplication) .build(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .build(); webApplication.service(request, response); assertNotNull(webApplication.getAttribute("testSessionCreated")); } /** * Test sessionCreated method. * * @throws Exception when a serious error occurs. */ @Test void testSessionDestroyed() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("TestSessionDestroyedServlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getSession().invalidate(); } }) .servletMapping("TestSessionDestroyedServlet", "/testSessionDestroyedServlet") .build(); webApplication.addListener(new HttpSessionListener() { @Override public void sessionDestroyed(HttpSessionEvent event) { event.getSession().getServletContext().setAttribute("testSessionDestroyed", true); } }); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .servletPath("/testSessionDestroyedServlet") .webApplication(webApplication) .build(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .build(); webApplication.service(request, response); assertNotNull(webApplication.getAttribute("testSessionDestroyed")); } /** * Test sessionIdChanged method. * * @throws Exception when a serious error occurs. */ @Test void testSessionIdChanged() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("TestSessionIdChangedServlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getSession(); request.changeSessionId(); } }) .servletMapping("TestSessionIdChangedServlet", "/testSessionIdChangedServlet") .build(); webApplication.addListener(new HttpSessionIdListener() { @Override public void sessionIdChanged(HttpSessionEvent event, String oldSessionId) { event.getSession().getServletContext().setAttribute("testSessionIdChanged", true); } }); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .servletPath("/testSessionIdChangedServlet") .webApplication(webApplication) .build(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .build(); webApplication.service(request, response); assertNotNull(webApplication.getAttribute("testSessionIdChanged")); } /** * Test valueBound method. * *

* Validate that an object implementing the HttpSessionBindingListener * interface gets notified when the value gets bound into the session. *

*/ @Test void testValueBound() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSessionManager manager = new DefaultHttpSessionManager(); DefaultHttpSession session = new DefaultHttpSession(webApplication); session.setSessionManager(manager); manager.attributeAdded(session, "name", new HttpSessionBindingListener() { @Override public void valueBound(HttpSessionBindingEvent event) { event.getSession().getServletContext().setAttribute("testAttributeAdded", true); } }); assertTrue((boolean) webApplication.getAttribute("testAttributeAdded")); } /** * Test valueUnbound method. * *

* Validate that an object implementing the HttpSessionBindingListener * interface gets notified when the value gets unbound from the session. *

*/ @Test void testValueUnbound() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSessionManager manager = new DefaultHttpSessionManager(); DefaultHttpSession session = new DefaultHttpSession(webApplication); session.setSessionManager(manager); manager.attributeRemoved(session, "name", new HttpSessionBindingListener() { @Override public void valueUnbound(HttpSessionBindingEvent event) { event.getSession().getServletContext().setAttribute("testAttributeRemoved", true); } }); assertTrue((boolean) webApplication.getAttribute("testAttributeRemoved")); } /** * Test valueBound and valueUnbound method. * *

* Validate that an object implementing the HttpSessionBindingListener * interface gets notified when the old value gets unbound. *

*

* Validate that an object implementing the HttpSessionBindingListener * interface gets notified when the new value gets bound. *

*/ @Test void testValueBoundUnbound() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSessionManager manager = new DefaultHttpSessionManager(); DefaultHttpSession session = new DefaultHttpSession(webApplication); session.setSessionManager(manager); Object value = new HttpSessionBindingListener() { @Override public void valueBound(HttpSessionBindingEvent event) { event.getSession().getServletContext().setAttribute("testAttributeReplacedA", true); } @Override public void valueUnbound(HttpSessionBindingEvent event) { event.getSession().getServletContext().setAttribute("testAttributeReplacedB", true); } }; manager.attributeReplaced(session, "name", value, value); assertTrue((boolean) webApplication.getAttribute("testAttributeReplacedA")); assertTrue((boolean) webApplication.getAttribute("testAttributeReplacedB")); } /** * Test getAttribute method. */ @Test void testGetAttribute() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSession session = new DefaultHttpSession(webApplication); session.setSessionManager(webApplication.getManager().getHttpSessionManager()); session.setAttribute("TestGetAttribute", "TestGetAttribute"); assertEquals("TestGetAttribute", session.getAttribute("TestGetAttribute")); session.removeAttribute("TestGetAttribute"); assertNull(session.getAttribute("TestGetAttribute")); } /** * Test getAttributeNames method. */ @Test void testGetAttributeNames() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSession session = new DefaultHttpSession(webApplication); session.setSessionManager(webApplication.getManager().getHttpSessionManager()); assertFalse(session.getAttributeNames().hasMoreElements()); } /** * Test getCookies method. */ @SuppressWarnings({"removal"}) @Test void testGetCookies() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSessionManager sessionManager = new DefaultHttpSessionManager(); sessionManager.setWebApplication(webApplication); TestWebApplicationRequest request = new TestWebApplicationRequest(); TestWebApplicationResponse response = new TestWebApplicationResponse(); webApplication.linkRequestAndResponse(request, response); sessionManager.setComment("Comment"); sessionManager.setDomain("domain"); sessionManager.setHttpOnly(true); sessionManager.setName("SessionCookie"); sessionManager.setMaxAge(100); sessionManager.setPath("/context"); sessionManager.setSecure(true); sessionManager.createSession(request); Cookie sessionCookie = response.getCookies() .stream() .filter(cookie -> "SessionCookie".equals(cookie.getName())) .findFirst() .orElse(null); assertNotNull(sessionCookie); assertEquals(sessionManager.getComment(), sessionCookie.getComment()); assertEquals(sessionManager.getDomain(), sessionCookie.getDomain()); assertTrue(sessionCookie.isHttpOnly()); assertEquals(sessionManager.getMaxAge(), sessionCookie.getMaxAge()); assertEquals(sessionManager.getPath(), sessionCookie.getPath()); assertTrue(sessionCookie.getSecure()); } /** * Test getCreationTime method. */ @Test void testGetCreationTime() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSession session = new DefaultHttpSession(webApplication); session.setSessionManager(webApplication.getManager().getHttpSessionManager()); assertTrue(session.getCreationTime() > 0); } /** * Test getId method. */ @Test void testGetId() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSession session = new DefaultHttpSession(webApplication); session.setSessionManager(webApplication.getManager().getHttpSessionManager()); assertNotNull(session.getId()); } /** * Test getLastAccessedTime method. */ @Test void testGetLastAccessedTime() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSession session = new DefaultHttpSession(webApplication); session.setSessionManager(webApplication.getManager().getHttpSessionManager()); assertTrue(session.getLastAccessedTime() >= session.getCreationTime()); } /** * Test getMaxInactiveInterval method. */ @Test void testGetMaxInactiveInterval() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSession session = new DefaultHttpSession(webApplication); session.setSessionManager(webApplication.getManager().getHttpSessionManager()); session.setMaxInactiveInterval(1000); assertEquals(1000, session.getMaxInactiveInterval()); } /** * Test getServletContext method. */ @Test void testGetServletContext() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSession session = new DefaultHttpSession(webApplication); session.setSessionManager(webApplication.getManager().getHttpSessionManager()); assertNotNull(session.getServletContext()); assertEquals(webApplication, session.getServletContext()); } /** * Test invalidate method. */ @Test void testInvalidate() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSession session = new DefaultHttpSession(webApplication); session.setSessionManager(webApplication.getManager().getHttpSessionManager()); session.invalidate(); try { session.isNew(); fail(); } catch (IllegalStateException expected) { } } /** * Test invalidate method. * * @throws IllegalStateException when the session is invalid. */ @Test void testInvalidate2() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSession session = new DefaultHttpSession(webApplication); session.setSessionManager(webApplication.getManager().getHttpSessionManager()); session.invalidate(); assertNotNull(assertThrows(IllegalStateException.class, () -> session.setAttribute("TEST", "TEST"))); } /** * Test isNew method. */ @Test void testIsNew() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSession session = new DefaultHttpSession(webApplication); session.setSessionManager(webApplication.getManager().getHttpSessionManager()); session.setNew(true); assertTrue(session.isNew()); session.setNew(false); assertFalse(session.isNew()); } /** * Test removeAttribute method. */ @Test void testRemoveAttribute() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSession session = new DefaultHttpSession(webApplication); session.setSessionManager(webApplication.getManager().getHttpSessionManager()); session.setAttribute("TestRemoveAttribute", "TestRemoveAttribute"); assertEquals("TestRemoveAttribute", session.getAttribute("TestRemoveAttribute")); session.removeAttribute("TestRemoveAttribute"); assertNull(session.getAttribute("TestRemoveAttribute")); } /** * Test setAttribute method. */ @Test void testSetAttribute() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSession session = new DefaultHttpSession(webApplication); session.setSessionManager(webApplication.getManager().getHttpSessionManager()); session.setAttribute("TestSetAttribute", "TestSetAttribute"); assertEquals("TestSetAttribute", session.getAttribute("TestSetAttribute")); } /** * Test setAttribute method. */ @Test void testSetAttribute2() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSession session = new DefaultHttpSession(webApplication); session.setSessionManager(webApplication.getManager().getHttpSessionManager()); session.setAttribute("TestSetAttribute", "TestSetAttribute"); session.setAttribute("TestSetAttribute", null); assertNull(session.getAttribute("TestSetAttribute")); } /** * Test setMaxInactiveInterval method. */ @Test void testSetMaxInactiveInterval() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSession session = new DefaultHttpSession(webApplication); session.setSessionManager(webApplication.getManager().getHttpSessionManager()); session.setMaxInactiveInterval(15); assertEquals(15, session.getMaxInactiveInterval()); } /** * Test ServletContextListener that sets the comment of the session cookie * config. */ public static class TestSetCommentListener implements ServletContextListener { @SuppressWarnings({"removal"}) @Override public void contextInitialized(ServletContextEvent event) { event.getServletContext().getSessionCookieConfig().setComment("MY COMMENT"); } } /** * Test HttpServlet to validate the servlet context listener can change * session cookie config values. */ public static class TestSetCommentServlet extends HttpServlet { @SuppressWarnings({"removal"}) @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (!request.getServletContext().getSessionCookieConfig() .getComment().equals("MY COMMENT")) { throw new ServletException("ServletContextListener did not work"); } } } /** * Test createSession method. */ @Test void testCreateSession() { DefaultHttpSessionManager sessionManager = new DefaultHttpSessionManager(); assertNotNull(assertThrows(NullPointerException.class, () -> sessionManager.createSession(null))); } /** * Test destroySession method. */ @Test void testDestroySession() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSessionManager manager = new DefaultHttpSessionManager(); DefaultHttpSession session = new DefaultHttpSession(webApplication); session.setSessionManager(manager); session.setAttribute("name", "value"); manager.addListener(new HttpSessionAttributeListener() { @Override public void attributeRemoved(HttpSessionBindingEvent event) { event.getSession().getServletContext().setAttribute("destroySession", true); } }); manager.destroySession(session); assertTrue((boolean) webApplication.getAttribute("destroySession")); } /** * Test encodeRedirectUrl method. */ @Test void testEncodeRedirectURL() { DefaultHttpSessionManager sessionManager = new DefaultHttpSessionManager(); assertEquals("test", sessionManager.encodeRedirectURL(null, "test")); } /** * Test encodeURL method. */ @Test void testEncodeURL() { DefaultHttpSessionManager sessionManager = new DefaultHttpSessionManager(); assertEquals("test", sessionManager.encodeURL(null, "test")); } /** * Test getComment method. */ @Test void testGetComment() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSessionManager sessionManager = new DefaultHttpSessionManager(); sessionManager.setWebApplication(webApplication); sessionManager.setComment("COMMENT"); assertNull(sessionManager.getComment()); } /** * Test getDomain method. */ @Test void testGetDomain() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSessionManager sessionManager = new DefaultHttpSessionManager(); sessionManager.setWebApplication(webApplication); sessionManager.setDomain("domain"); assertEquals("domain", sessionManager.getDomain()); } /** * Test getEffectivfeSessionTrackingModes method. */ @Test void testGetEffectiveSessionTrackingModes() { DefaultHttpSessionManager sessionManager = new DefaultHttpSessionManager(); assertEquals(sessionManager.getDefaultSessionTrackingModes(), sessionManager.getEffectiveSessionTrackingModes()); } /** * Test getMaxAge method. */ @Test void testGetMaxAge() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSessionManager sessionManager = new DefaultHttpSessionManager(); sessionManager.setWebApplication(webApplication); assertEquals(-1, sessionManager.getMaxAge()); sessionManager.setMaxAge(60); assertEquals(60, sessionManager.getMaxAge()); } /** * Test getName method. */ @Test void testGetName() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSessionManager sessionManager = new DefaultHttpSessionManager(); sessionManager.setWebApplication(webApplication); sessionManager.setName("JSESSIONID"); assertEquals("JSESSIONID", sessionManager.getName()); } /** * Test getPath method. */ @Test void testGetPath() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSessionManager sessionManager = new DefaultHttpSessionManager(); sessionManager.setWebApplication(webApplication); sessionManager.setPath("/"); assertEquals("/", sessionManager.getPath()); } /** * Test getSession method. */ @Test void testGetSession() { DefaultHttpSessionManager sessionManager = new DefaultHttpSessionManager(); assertNotNull(assertThrows(NullPointerException.class, () -> sessionManager.getSession(null, null))); } /** * Test getSession method. */ @Test void testGetSession2() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSessionManager sessionManager = new DefaultHttpSessionManager(); sessionManager.setWebApplication(webApplication); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); webApplication.linkRequestAndResponse(request, response); HttpSession session = sessionManager.createSession(request); request.setRequestedSessionId(session.getId()); assertNotNull(sessionManager.getSession(request, session.getId())); } /** * Test getSessionCookieConfig method. */ @Test void testGetSessionCookieConfig() { DefaultHttpSessionManager sessionManager = new DefaultHttpSessionManager(); assertNotNull(sessionManager.getSessionCookieConfig()); } /** * Test getSessionTimeout method. */ @Test void testGetSessionTimeout() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSessionManager sessionManager = new DefaultHttpSessionManager(); sessionManager.setWebApplication(webApplication); assertEquals(10, sessionManager.getSessionTimeout()); sessionManager.setSessionTimeout(5); assertEquals(5, sessionManager.getSessionTimeout()); } /** * Test isHttpOnly method. */ @Test void testIsHttpOnly() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSessionManager sessionManager = new DefaultHttpSessionManager(); sessionManager.setWebApplication(webApplication); assertFalse(sessionManager.isHttpOnly()); sessionManager.setHttpOnly(true); assertTrue(sessionManager.isHttpOnly()); } /** * Test isSecure method. */ @Test void testIsSecure() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSessionManager sessionManager = new DefaultHttpSessionManager(); sessionManager.setWebApplication(webApplication); assertFalse(sessionManager.isSecure()); sessionManager.setSecure(true); assertTrue(sessionManager.isSecure()); } /** * Test setComment. * * @throws Exception when a serious error occurs. */ @SuppressWarnings({"removal"}) @Test void testSetComment() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultHttpSessionManager manager = (DefaultHttpSessionManager) webApplication.getManager().getHttpSessionManager(); manager.setComment("Comment"); assertNull(webApplication.getSessionCookieConfig().getComment()); } /** * Test setComment. */ @SuppressWarnings({"removal"}) @Test void testSetComment2() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initialize(); webApplication.start(); assertNotNull(assertThrows(IllegalStateException.class, () -> webApplication.getManager().getHttpSessionManager() .getSessionCookieConfig().setComment("Comment"))); webApplication.stop(); } /** * Test setDomain. * *

* Validate we are throwing an IllegalStateException once the ServletContext * has been initialized. *

*/ @Test void testSetDomain() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initialize(); webApplication.start(); assertNotNull(assertThrows(IllegalStateException.class, () -> webApplication.getManager().getHttpSessionManager() .getSessionCookieConfig().setDomain("Domain"))); webApplication.stop(); } /** * Test setHttpOnly. * *

* Validate we are throwing an IllegalStateException once the ServletContext * has been initialized. *

*/ @Test void testSetHttpOnly() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initialize(); webApplication.start(); assertNotNull(assertThrows(IllegalStateException.class, () -> webApplication.getManager().getHttpSessionManager() .getSessionCookieConfig().setHttpOnly(true))); webApplication.stop(); } /** * Test setMaxAge. * *

* Validate we are throwing an IllegalStateException once the ServletContext * has been initialized. *

*/ @Test void testSetMaxAge() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initialize(); webApplication.start(); assertNotNull(assertThrows(IllegalStateException.class, () -> webApplication.getManager().getHttpSessionManager() .getSessionCookieConfig().setMaxAge(1234))); webApplication.stop(); } /** * Test setName. * *

* Validate we are throwing an IllegalStateException once the ServletContext * has been initialized. *

*/ @Test void testSetName() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initialize(); webApplication.start(); assertNotNull(assertThrows(IllegalStateException.class, () -> webApplication.getManager().getHttpSessionManager() .getSessionCookieConfig().setName("Name"))); webApplication.stop(); } /** * Test setPath. * *

* Validate we are throwing an IllegalStateException once the ServletContext * has been initialized. *

*/ @Test void testSetPath() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initialize(); webApplication.start(); assertNotNull(assertThrows(IllegalStateException.class, () -> webApplication.getManager().getHttpSessionManager() .getSessionCookieConfig().setPath("/path"))); webApplication.stop(); } /** * Test setSecure. * *

* Validate we are throwing an IllegalStateException once the ServletContext * has been initialized. *

*/ @Test void testSetSecure() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.initialize(); webApplication.start(); assertNotNull(assertThrows(IllegalStateException.class, () -> webApplication.getManager().getHttpSessionManager() .getSessionCookieConfig().setSecure(true))); webApplication.stop(); } /** * Test setSessionTrackingModes method. */ @Test void testSetSessionTrackingModes() { DefaultHttpSessionManager sessionManager = new DefaultHttpSessionManager(); EnumSet sslAndUrl = EnumSet.of(SSL, URL); assertNotNull(assertThrows(IllegalArgumentException.class, () -> sessionManager.setSessionTrackingModes(sslAndUrl))); EnumSet sslAndCookie = EnumSet.of(COOKIE, SSL); assertNotNull(assertThrows(IllegalArgumentException.class, () -> sessionManager.setSessionTrackingModes(sslAndCookie))); } /** * Test changeSessionId method. */ @Test void testChangeSessionId() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); HttpSession session = request.getSession(true); String sessionId1 = session.getId(); request.setRequestedSessionId(session.getId()); String sessionId2 = request.changeSessionId(); assertNotEquals(sessionId1, sessionId2); } /** * Test changeSessionId method. */ @Test void testChangeSessionId2() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); HttpSession session = request.getSession(true); String sessionId1 = session.getId(); request.setRequestedSessionId(session.getId()); String sessionId2 = request.changeSessionId(); assertNotEquals(sessionId1, sessionId2); } /** * Test changeSessionId method. */ @Test void testChangeSessionId3() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); assertNotNull(assertThrows(IllegalStateException.class, request::changeSessionId)); } /** * Test changeSessionId method. */ @Test void testChangeSessionId4() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); HttpSession session = request.getSession(true); String previousSessionId = session.getId(); String newSessionId = request.changeSessionId(); assertNotEquals(previousSessionId, newSessionId); assertEquals(newSessionId, request.getSession(false).getId()); } /** * Test getSession method. */ @Test void testGetSession3() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); HttpSession session = request.getSession(); assertNotNull(session.getId()); assertTrue(session.isNew()); } /** * Test getSession method. */ @Test void testGetSession4() { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); HttpSession session = request.getSession(false); assertNull(session); } /** * Test createListener method to create a HttpSessionListener. * * @throws Exception when a serious error occurs. */ @Test void testCreateListenerWithHttpSessionListener() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .build(); assertNotNull(webApplication.createListener( TestCreateListenerWithHttpSessionListener.class)); } /** * Test getSessionCookieConfig method is not null. */ @Test void testGetSessionCookieConfigIsNotNull() { WebApplication webApplication = new DefaultWebApplicationBuilder() .build(); assertNotNull(webApplication.getSessionCookieConfig()); } /** * Test getSessionTimeout method to see if it returns a value > 0. */ @Test void testGetSessionTimeoutToSeeIfValueIsGreaterThanZero() { WebApplication webApplication = new DefaultWebApplicationBuilder() .build(); assertTrue(webApplication.getSessionTimeout() > 0); } /** * Test getSession method when passing in false returns null. */ @Test void testGetSessionWhenPassingInFalseReturnsNull() { DefaultWebApplication webApplication = new DefaultWebApplicationBuilder() .build(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .build(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .build(); webApplication.linkRequestAndResponse(request, response); HttpSession session = request.getSession(false); assertNull(session); } /** * Test getSession method when passing in true and then passing in false * returns not null on the last call. */ @Test void testGetSessionWhenPassingInTrueAndFalseReturnsNotNull() { DefaultWebApplication webApplication = new DefaultWebApplicationBuilder() .build(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .build(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .build(); webApplication.linkRequestAndResponse(request, response); HttpSession session = request.getSession(true); request.setRequestedSessionId(session.getId()); assertNotNull(request.getSession(false)); } /** * Test getSession method when passing in true returns not null. */ @Test void testGetSessionWhenPassingInTrueReturnNotNull() { DefaultWebApplication webApplication = new DefaultWebApplicationBuilder() .build(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .build(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .build(); webApplication.linkRequestAndResponse(request, response); HttpSession session = request.getSession(true); request.setRequestedSessionId(session.getId()); assertNotNull(request.getSession()); } /** * Test getSession without parameters creates a new session. * * @throws Exception */ @Test void testGetSessionWithoutParameters() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplicationBuilder() .build(); ServletRegistration.Dynamic dynamic = webApplication.addServlet("session", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType("text/plain"); try (PrintWriter out = response.getWriter()) { if (request.isRequestedSessionIdValid()) { out.println("FAILED as session is already active"); } else { HttpSession session = request.getSession(); out.println("New session is " + session); } } } }); assertNotNull(dynamic); dynamic.addMapping("/session"); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .servletPath("/session") .build(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .bodyOnly(true) .underlyingOutputStream(byteOutput) .build(); webApplication.service(request, response); assertTrue(byteOutput.toString().contains("New session is")); } /** * Test isRequestedSessionIdFromCookie method to validate it returns false * by default. */ @Test void testIsRequestedSessionIdFromCookie() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .build(); assertFalse(request.isRequestedSessionIdFromCookie()); } /** * Test isRequestedSessionIdFromURL method to validate it returns false by * default. */ @Test void testIsRequestedSessionIdFromURLReturnsFalseByDefault() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .build(); assertFalse(request.isRequestedSessionIdFromURL()); } /** * Test isRequestedSessionIdValid method to return false by default. */ @Test void testIsRequestedSessionIdValidReturnsFalseByDefault() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .build(); assertFalse(request.isRequestedSessionIdValid()); } /** * Test isRequestedSessionIdValid method returns true when requested session * id has been set by a call to setRequestedSessionId. */ @Test void testIsRequestedSessionIdValidReturnsTrue() { DefaultWebApplication webApplication = new DefaultWebApplicationBuilder() .build(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .build(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .build(); webApplication.linkRequestAndResponse(request, response); HttpSession session = request.getSession(true); request.setRequestedSessionId(session.getId()); assertTrue(request.isRequestedSessionIdValid()); } /** * Test setRequestedSessionIdFromCookie method by passing in true. */ @Test void testSetRequestedSessionIdFromCookieToTrue() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .build(); assertFalse(request.isRequestedSessionIdFromCookie()); request.setRequestedSessionIdFromCookie(true); assertTrue(request.isRequestedSessionIdFromCookie()); } /** * Test setRequestedSessionIdFromURL method by passing in true. */ @Test void testSetRequestedSessionIdFromURLToTrue() { DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .build(); assertFalse(request.isRequestedSessionIdFromURL()); request.setRequestedSessionIdFromURL(true); assertTrue(request.isRequestedSessionIdFromURL()); } /** * Test setSessionTimeout method throws an IllegalStateException after the * web application has been started. */ @Test void testSetSessionTimeoutThrowsIllegalStateExceptionAfterStart() { WebApplication webApplication = new DefaultWebApplicationBuilder() .build() .initialize() .start(); assertNotNull(assertThrows(IllegalStateException.class, () -> webApplication.setSessionTimeout(50))); } /** * Test setSessionTimeout method with multiple timeouts. */ @Test void testSetSessionTimeoutWithMultipleTimeouts() { WebApplication webApplication = new DefaultWebApplicationBuilder() .build(); webApplication.setSessionTimeout(5); assertEquals(5, webApplication.getSessionTimeout()); webApplication.setSessionTimeout(10); assertEquals(10, webApplication.getSessionTimeout()); } /** * Test setSessionTrackingModes method with an empty HashSet. */ @Test void testSetSessionTrackingModesWithEmptyHashSet() { @SuppressWarnings({ "rawtypes", "unchecked" }) WebApplication webApplication = new DefaultWebApplicationBuilder() .sessionTrackingModes(new HashSet()) .build(); assertTrue(webApplication.getEffectiveSessionTrackingModes().isEmpty()); } /** * Test HttpSessionListener to validate createListener was called. */ public static class TestCreateListenerWithHttpSessionListener implements HttpSessionListener { } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/ReadListenerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationRequest; import cloud.piranha.core.api.WebApplicationResponse; import jakarta.servlet.ReadListener; import jakarta.servlet.ServletInputStream; import java.io.ByteArrayInputStream; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.Test; /** * The JUnit tests for the ReadListener API. * * @author Manfred Riem (mriem@manorrock.com) */ public class ReadListenerTest { private WebApplication createWebApplication() { return new DefaultWebApplication(); } private WebApplicationRequest createWebApplicationRequest() { return new DefaultWebApplicationRequest(); } private WebApplicationResponse createWebApplicationResponse() { return new DefaultWebApplicationResponse(); } /** * Test onAllDataRead method. * * @throws Exception when a serious error occurs. */ @Test void testOnAllDataRead() throws Exception { WebApplication webApplication = createWebApplication(); WebApplicationRequest request = createWebApplicationRequest(); request.setAsyncSupported(true); request.setWebApplication(webApplication); WebApplicationResponse response = createWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); request.startAsync(); try (ServletInputStream inputStream = request.getInputStream()) { request.getWebApplicationInputStream().setInputStream(new ByteArrayInputStream("read this".getBytes())); inputStream.setReadListener(new TestOnAllDataReadReadListener(webApplication)); int character = inputStream.read(); while(character != -1) { character = inputStream.read(); } } Thread.sleep(2000); assertNotNull(webApplication.getAttribute("onAllDataRead")); } /** * Test onDataAvailable method. * * @throws Exception when a serious error occurs. */ @Test void testOnDataAvailable() throws Exception { WebApplication webApplication = createWebApplication(); WebApplicationRequest request = createWebApplicationRequest(); request.setAsyncSupported(true); request.setWebApplication(webApplication); WebApplicationResponse response = createWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); request.startAsync(); ServletInputStream inputStream = request.getInputStream(); request.getWebApplicationInputStream().setInputStream(new ByteArrayInputStream("a".getBytes())); inputStream.setReadListener(new TestOnDataAvailableReadListener(webApplication)); Thread.sleep(5000); assertNotNull(webApplication.getAttribute("onDataAvailable")); } /** * A test ReadListener for onAllDataRead. */ public static class TestOnAllDataReadReadListener implements ReadListener { /** * Stores the web application. */ private final WebApplication webApplication; /** * Constructor. * * @param webApplication the web application. */ public TestOnAllDataReadReadListener(WebApplication webApplication) { this.webApplication = webApplication; } @Override public void onAllDataRead() throws IOException { webApplication.setAttribute("onAllDataRead", true); } @Override public void onDataAvailable() throws IOException { } @Override public void onError(Throwable t) { } } /** * A test ReadListener for onDataAvailable. */ public static class TestOnDataAvailableReadListener implements ReadListener { /** * Stores the web application. */ private final WebApplication webApplication; /** * Constructor. * * @param webApplication the web application. */ public TestOnDataAvailableReadListener(WebApplication webApplication) { this.webApplication = webApplication; } @Override public void onAllDataRead() throws IOException { } @Override public void onDataAvailable() throws IOException { webApplication.setAttribute("onDataAvailable", true); } @Override public void onError(Throwable t) { } } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/RequestDispatcherTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletException; import jakarta.servlet.UnavailableException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import org.junit.jupiter.api.Test; import cloud.piranha.core.api.WebApplication; import static jakarta.servlet.RequestDispatcher.FORWARD_CONTEXT_PATH; import static jakarta.servlet.RequestDispatcher.FORWARD_MAPPING; import static jakarta.servlet.RequestDispatcher.FORWARD_PATH_INFO; import static jakarta.servlet.RequestDispatcher.FORWARD_QUERY_STRING; import static jakarta.servlet.RequestDispatcher.FORWARD_REQUEST_URI; import static jakarta.servlet.RequestDispatcher.FORWARD_SERVLET_PATH; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; /** * The JUnit tests for the RequestDispatcher API. * * @author Manfred Riem (mriem@manorrock.com) */ class RequestDispatcherTest { /** * Test forward method with a named RequestDispatcher. * *

* This test validates Servlet:SPEC:181, Servlet:SPEC:181.1, * Servlet:SPEC:181.2, Servlet:SPEC:181.3, Servlet:SPEC:181.4 and * Servlet:SPEC:181.5 *

* * @throws Exception when a serious error occurs. */ @Test void testForwardWithNamedDispatcher() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("TestNamedForwardServlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter writer = response.getWriter(); if (request.getAttribute(FORWARD_CONTEXT_PATH) != null) { writer.println(request.getAttribute(FORWARD_CONTEXT_PATH)); } if (request.getAttribute(FORWARD_MAPPING) != null) { writer.println(request.getAttribute(FORWARD_CONTEXT_PATH)); } if (request.getAttribute(FORWARD_PATH_INFO) != null) { writer.println(request.getAttribute(FORWARD_PATH_INFO)); } if (request.getAttribute(FORWARD_QUERY_STRING) != null) { writer.println(request.getAttribute(FORWARD_QUERY_STRING)); } if (request.getAttribute(FORWARD_REQUEST_URI) != null) { writer.println(request.getAttribute(FORWARD_REQUEST_URI)); } if (request.getAttribute(FORWARD_SERVLET_PATH) != null) { writer.println(request.getAttribute(FORWARD_SERVLET_PATH)); } writer.flush(); } }) .build() .initialize() .start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .build(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .bodyOnly(true) .underlyingOutputStream(byteOutput) .build(); webApplication.linkRequestAndResponse(request, response); RequestDispatcher dispatcher = webApplication.getNamedDispatcher("TestNamedForwardServlet"); dispatcher.forward(request, response); assertEquals("", new String(byteOutput.toByteArray())); } /** * Test forward method on a named RequestDispatcher. * *

* This test validates Servlet:SPEC:79 *

* * @throws Exception when a serious error occurs. */ @Test void testNamedForward2() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("TestNamedForward2Servlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter writer = response.getWriter(); writer.print(request.getRequestURI()); writer.flush(); } }) .build() .initialize() .start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .build(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .bodyOnly(true) .underlyingOutputStream(byteOutput) .build(); webApplication.linkRequestAndResponse(request, response); RequestDispatcher dispatcher = webApplication.getNamedDispatcher("TestNamedForward2Servlet"); dispatcher.forward(request, response); assertEquals("/", new String(byteOutput.toByteArray())); } /** * Test forward method on a named RequestDispatcher. * *

* This test validates Servlet:SPEC:80 *

* * @throws Exception when a serious error occurs. */ @Test void testNamedForward3() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("TestNamedForward3Servlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter writer = response.getWriter(); writer.print("SUCCESS"); } }) .build() .initialize() .start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .build(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .bodyOnly(true) .underlyingOutputStream(byteOutput) .build(); webApplication.linkRequestAndResponse(request, response); RequestDispatcher dispatcher = webApplication.getNamedDispatcher("TestNamedForward3Servlet"); dispatcher.forward(request, response); assertEquals("SUCCESS", new String(byteOutput.toByteArray())); } /** * Test forward method on a named RequestDispatcher. * * @throws Exception when a serious error occurs. */ @Test void testNamedForward4() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("TestNamedForward4Servlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter writer = response.getWriter(); writer.println("FAILED"); } }) .build() .initialize() .start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .build(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .bodyOnly(true) .underlyingOutputStream(byteOutput) .build(); response.flushBuffer(); RequestDispatcher dispatcher = webApplication.getNamedDispatcher("TestNamedForward4Servlet"); try { dispatcher.forward(request, response); fail(); } catch (IllegalStateException ise) { } } /** * Test include method on a named RequestDispatcher. * *

* This test validates Servlet:SPEC:192.1 *

* * @throws Exception when a serious error occurs. */ @Test void testNamedInclude() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("TestNamedIncludeServlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter writer = response.getWriter(); response.setStatus(202); response.setHeader("header", "value"); writer.println("INCLUDED"); } }) .build() .initialize() .start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .build(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .underlyingOutputStream(byteOutput) .build(); webApplication.linkRequestAndResponse(request, response); RequestDispatcher dispatcher = webApplication.getNamedDispatcher("TestNamedIncludeServlet"); dispatcher.include(request, response); response.flushBuffer(); assertTrue(new String(byteOutput.toByteArray()).contains("HTTP/1.1")); assertFalse(new String(byteOutput.toByteArray()).contains("202")); assertFalse(new String(byteOutput.toByteArray()).contains("header")); assertFalse(new String(byteOutput.toByteArray()).contains("value")); assertTrue(new String(byteOutput.toByteArray()).contains("INCLUDED")); } /** * Test forward method. * * @throws Exception when an error occurs. */ @Test void testForward() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("Forward", new HttpServlet() { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter out = resp.getWriter(); out.println("Forward"); out.flush(); } }) .servletMapping("Forward", "/forward") .build() .initialize() .start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .build(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .bodyOnly(true) .underlyingOutputStream(byteOutput) .build(); RequestDispatcher dispatcher = webApplication.getRequestDispatcher("/forward"); dispatcher.forward(request, response); String responseText = new String(byteOutput.toByteArray()); assertTrue(responseText.contains("Forward")); } /** * Test forward method. * * @throws Exception when an error occurs. */ @Test void testForward3() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("Error", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { throw new IOException(); } }) .servletMapping("Error", "/error") .build() .initialize() .start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .build(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .build(); RequestDispatcher dispatcher = webApplication.getRequestDispatcher("/error"); assertThrows(IOException.class, () -> dispatcher.forward(request, response)); } /** * Test forward method. * * @throws Exception when an error occurs. */ @Test void testForward4() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("Runtime", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { throw new RuntimeException(); } }) .servletMapping("Runtime", "/runtime") .build() .initialize() .start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .build(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .build(); RequestDispatcher dispatcher = webApplication.getRequestDispatcher("/runtime"); assertThrows(RuntimeException.class, () -> dispatcher.forward(request, response)); } /** * Test that a request given to the request dispatcher upon forward is the * same as the original request. * * @throws Exception when an error occurs. */ @Test void testForwardNoWrapping() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("NoWrapping", new HttpServlet() { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().print(req.toString()); } }) .servletMapping("NoWrapping", "/nowrapping") .build() .initialize() .start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .build(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .bodyOnly(true) .underlyingOutputStream(byteOutput) .build(); RequestDispatcher dispatcher = webApplication.getRequestDispatcher("/nowrapping"); dispatcher.forward(request, response); assertEquals(request.toString(), byteOutput.toString("UTF-8")); } /** * Test a forward using a named dispatcher. * * @throws Exception when an error occurs. */ @Test void testForwardWithNamedDispatcher2() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("Forward", new HttpServlet() { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter out = resp.getWriter(); out.println("Forward"); } }) .servletMapping("Forward", "/forward") .build() .initialize() .start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .build(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .bodyOnly(true) .underlyingOutputStream(byteOutput) .build(); RequestDispatcher dispatcher = webApplication.getNamedDispatcher("Forward"); dispatcher.forward(request, response); response.flushBuffer(); String responseText = new String(byteOutput.toByteArray()); assertTrue(responseText.contains("Forward")); } /** * Test a forward with a query string. * * @throws Exception when an error occurs. */ @Test void testForwardWithQueryString() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("Forward", new HttpServlet() { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { /* * Force the original parameter to be parsed out of the query string. */ req.getParameter("p"); /* * Dispatch with a new parameter value. */ getServletContext() .getRequestDispatcher("/forward2?p=New") .forward(req, resp); } }) .servletMapping("Forward", "/forward") .servlet("Forward2", new HttpServlet() { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { /* * We should be getting the new parameter value here as it takes precendence. */ resp.getWriter().print(req.getParameterMap().get("p")[0]); } }) .servletMapping("Forward2", "/forward2") .build() .initialize() .start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .servletPath("/forward") .queryString("p=Original") .build(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .bodyOnly(true) .underlyingOutputStream(byteOutput) .build(); webApplication.service(request, response); assertEquals("New", byteOutput.toString()); } /** * Test the getNamedDispatcher method on WebApplication instance with an * existing Servlet. */ @Test void testGetExistingNamedDispatcher() { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("ExistingNamedDispatcher", new HttpServlet() { }) .build() .initialize() .start(); assertNotNull(webApplication.getNamedDispatcher("ExistingNamedDispatcher")); } /** * Test getNamedDispatcher method on WebApplication instance with a * non-existing Servlet. */ @Test void testGetNonExistingNamedDispatcher() { WebApplication webApplication = new DefaultWebApplicationBuilder() .build() .initialize() .start(); assertNull(webApplication.getNamedDispatcher("NotExistingNamedDispatcher")); } /** * Test include method using a writer works properly. * * @throws Exception when a serious error occurs. */ @Test void testInclude() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("Writer", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.getWriter().print("Writer"); response.getWriter().flush(); } }) .build() .initialize() .start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .build(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .bodyOnly(true) .underlyingOutputStream(byteOutput) .build(); RequestDispatcher dispatcher = webApplication.getNamedDispatcher("Writer"); dispatcher.include(request, response); response.flushBuffer(); assertTrue(byteOutput.toString().contains("Writer")); } /** * Test include method using an output stream write works properly. * * @throws Exception when a serious error occurs. */ @Test void testIncludeOutputStream() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("OutputStream", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.getOutputStream().write("OutputStream".getBytes()); response.getOutputStream().flush(); } }) .build() .initialize() .start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .build(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .bodyOnly(true) .underlyingOutputStream(byteOutput) .build(); RequestDispatcher dispatcher = webApplication.getNamedDispatcher("OutputStream"); dispatcher.include(request, response); response.flushBuffer(); assertTrue(byteOutput.toString().contains("OutputStream")); } /** * Test that a request given to the request dispatcher upon include is the * same as the original request. */ @Test void testIncludeNoWrapping() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("NoWrapping", new HttpServlet() { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getRequestDispatcher("/nowrapping2").include(req, resp); resp.flushBuffer(); } }) .servletMapping("NoWrapping", "/nowrapping") .servlet("NoWrapping2", new HttpServlet() { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().print(req.hashCode()); } }) .servletMapping("NoWrapping2", "/nowrapping2") .build() .initialize() .start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .build(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .bodyOnly(true) .underlyingOutputStream(byteOutput) .build(); RequestDispatcher dispatcher = webApplication.getRequestDispatcher("/nowrapping"); dispatcher.forward(request, response); assertEquals(request.hashCode() + "", byteOutput.toString("UTF-8")); } /** * Test include method using named RequestDispatcher and validate it throws * an IllegalStateException when the response is already committed. * *

* This test validates Servlet:SPEC:192.3 and Servlet:SPEC:192.4 *

* * @throws Exception when a serious error occurs. */ @Test void testIncludeWitNamedRequestDispatcherAndIllegalStateException() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("IllegalStateException", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter writer = response.getWriter(); response.flushBuffer(); writer.println(request.getSession().getId()); } }) .build() .initialize() .start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .build(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .bodyOnly(true) .build(); webApplication.linkRequestAndResponse(request, response); RequestDispatcher dispatcher = webApplication.getNamedDispatcher("IllegalStateException"); try { dispatcher.include(request, response); fail(); } catch (IllegalStateException ise) { assertTrue(ise.getMessage().contains("Response already committed")); } } /** * Test include method using a named RequestDispatcher and a fixed buffer * size to validate the response was committed after buffer overflow * happened. * *

* This test validates Servlet:SPEC:192.2 *

* * @throws Exception when a serious error occurs. */ @Test void testIncludeWitNamedRequestDispatcherAndBufferSize() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("BufferSize", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { OutputStream output = response.getOutputStream(); output.write('1'); output.write('2'); output.write('3'); output.write('4'); output.write('5'); if (response.isCommitted()) { output.write('6'); } else { throw new ServletException("Response was not committed"); } } }) .build() .initialize() .start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .build(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .bodyOnly(true) .underlyingOutputStream(byteOutput) .build(); response.setBufferSize(5); webApplication.linkRequestAndResponse(request, response); RequestDispatcher dispatcher = webApplication.getNamedDispatcher("BufferSize"); dispatcher.include(request, response); response.flushBuffer(); assertEquals("123456", new String(byteOutput.toByteArray())); } /** * Test an include with a query string. * * @throws Exception when an error occurs. */ @Test void testIncludeWithQueryString() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("Include", new HttpServlet() { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { /* * Force the original parameter to be parsed out of the query string. */ req.getParameter("p"); /* * Dispatch with a new parameter value. */ getServletContext() .getRequestDispatcher("/include2?p=New") .include(req, resp); } }) .servletMapping("Include", "/include") .servlet("Include2", new HttpServlet() { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { /* * We should be getting the new parameter value here as it takes precendence. */ resp.getWriter().print(req.getParameterMap().get("p")[0]); } }) .servletMapping("Include2", "/include2") .build() .initialize() .start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .servletPath("/include") .queryString("p=Original") .build(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .bodyOnly(true) .underlyingOutputStream(byteOutput) .build(); webApplication.service(request, response); assertEquals("New", byteOutput.toString()); } @Test void testErrorDispatcher() throws Exception { WebApplication webApp = new DefaultWebApplicationBuilder() .servlet("error-servlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if (request.getParameter("send-error") != null) { response.sendError(500, "some-internal-error"); return; } throw new UnavailableException("unavailable"); } }) .servletMapping("error-servlet", "/sendError") .servlet("snoop", TestSnoopServlet.class) .servletMapping("snoop", "/snoop") .build(); webApp.getManager().getErrorPageManager().addErrorPage(500, "/snoop"); webApp.initialize(); webApp.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .servletPath("/sendError") .build(); request.setParameter("send-error", new String[]{"true"}); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApp) .bodyOnly(true) .underlyingOutputStream(byteOutput) .build(); webApp.service(request, response); String responseText = new String(byteOutput.toByteArray()); assertTrue(responseText.contains(RequestDispatcher.ERROR_MESSAGE)); assertTrue(responseText.contains("some-internal-error")); } @Test void testErrorDispatcher2() throws Exception { WebApplication webApp = new DefaultWebApplicationBuilder() .servlet("error-servlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { throw new IOException(); } }) .servletMapping("error-servlet", "/sendError") .build(); webApp.addServlet("snoop", TestSnoopServlet.class); webApp.addServletMapping("snoop", "/snoop"); webApp.getManager().getErrorPageManager().addErrorPage(IOException.class.getName(), "/snoop"); webApp.initialize(); webApp.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApp) .servletPath("/sendError") .build(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApp) .bodyOnly(true) .underlyingOutputStream(byteOutput) .build(); webApp.service(request, response); String responseText = new String(byteOutput.toByteArray()); assertEquals(500, response.getStatus()); assertTrue(responseText.contains(RequestDispatcher.ERROR_EXCEPTION_TYPE)); assertTrue(responseText.contains(IOException.class.getName())); } /** * Test getRequestDispatcher method. */ @Test void testGetRequestDispatcher() { WebApplication webApplication = new DefaultWebApplicationBuilder() .build() .initialize() .start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .build(); assertNotNull(request.getRequestDispatcher("/test")); } /** * Test getRequestDispatcher method. */ @Test void testGetRequestDispatcher2() { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("TestGetRequestDispatcherServlet", new HttpServlet() { }) .servletMapping("TestGetRequestDispatcherServlet", "testGetRequestDispatcher") .build() .initialize() .start(); assertNotNull(webApplication.getRequestDispatcher("/testGetRequestDispatcher")); } /** * Test include. * * @throws Exception when a serious error occurred. */ @Test void testInclude2() throws Exception { WebApplication webApplication = new DefaultWebApplicationBuilder() .servlet("Include", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { RequestDispatcher dispatcher = request .getServletContext().getRequestDispatcher("/include2"); dispatcher.include(request, response); } }) .servletMapping("Include", "/include") .servlet("Include2", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.getWriter().print("This was included"); response.getWriter().flush(); } }) .servletMapping("Include2", "/include2") .build() .initialize() .start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequestBuilder() .webApplication(webApplication) .servletPath("/include") .build(); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponseBuilder() .webApplication(webApplication) .underlyingOutputStream(byteOutput) .build(); webApplication.service(request, response); assertTrue(new String(byteOutput.toByteArray()).contains("This was included")); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/ServletContextAttributeListenerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationRequest; import cloud.piranha.core.api.WebApplicationResponse; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletContextAttributeEvent; import jakarta.servlet.ServletContextAttributeListener; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.Test; /** * The JUnit tests for the ServletContextAttributeListener API. * * @author Manfred Riem (mriem@manorrock.com) */ class ServletContextAttributeListenerTest { private WebApplication createWebApplication() { return new DefaultWebApplication(); } private WebApplicationRequest createWebApplicationRequest() { return new DefaultWebApplicationRequest(); } private WebApplicationResponse createWebApplicationResponse() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.getWebApplicationOutputStream().setOutputStream(new ByteArrayOutputStream()); return response; } /** * Test attributeAdded method. * * @throws Exception when a serious error occurs. */ @Test void testAttributeAdded() throws Exception { WebApplication webApplication = createWebApplication(); webApplication.addListener(new ServletContextAttributeListener() { @Override public void attributeAdded(ServletContextAttributeEvent event) { event.getServletContext().setAttribute("testAttributeAdded", true); } }); webApplication.addServlet("TestAttributeAddedServlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getServletContext().setAttribute("name", "value"); } }); webApplication.addServletMapping( "TestAttributeAddedServlet", "/testAttributeAdded"); WebApplicationRequest request = createWebApplicationRequest(); request.setServletPath("/testAttributeAdded"); request.setWebApplication(webApplication); WebApplicationResponse response = createWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.initialize(); webApplication.start(); webApplication.service(request, response); assertNotNull(webApplication.getAttribute("testAttributeAdded")); webApplication.stop(); } /** * Test attributeRemoved method. * * @throws Exception when a serious error occurs. */ @Test void testAttributeRemoved() throws Exception { WebApplication webApplication = createWebApplication(); webApplication.addListener(new ServletContextAttributeListener() { @Override public void attributeRemoved(ServletContextAttributeEvent event) { event.getServletContext().setAttribute("testAttributeRemoved", true); } }); webApplication.addServlet("TestAttributeRemovedServlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext context = request.getServletContext(); context.setAttribute("name", "value"); context.removeAttribute("name"); } }); webApplication.addServletMapping( "TestAttributeRemovedServlet", "/testAttributeRemoved"); WebApplicationRequest request = createWebApplicationRequest(); request.setServletPath("/testAttributeRemoved"); request.setWebApplication(webApplication); WebApplicationResponse response = createWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.initialize(); webApplication.start(); webApplication.service(request, response); assertNotNull(webApplication.getAttribute("testAttributeRemoved")); webApplication.stop(); } /** * Test attributeReplaced method. * * @throws Exception when a serious error occurs. */ @Test void testAttributeReplaced() throws Exception { WebApplication webApplication = createWebApplication(); webApplication.addListener(new ServletContextAttributeListener() { @Override public void attributeReplaced(ServletContextAttributeEvent event) { event.getServletContext().setAttribute("testAttributeReplaced", true); } }); webApplication.addServlet("TestAttributeReplacedServlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext context = request.getServletContext(); context.setAttribute("name", "value"); context.setAttribute("name", "value2"); } }); webApplication.addServletMapping( "TestAttributeReplacedServlet", "/testAttributeReplaced"); WebApplicationRequest request = createWebApplicationRequest(); request.setServletPath("/testAttributeReplaced"); request.setWebApplication(webApplication); WebApplicationResponse response = createWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.initialize(); webApplication.start(); webApplication.service(request, response); assertNotNull(webApplication.getAttribute("testAttributeReplaced")); webApplication.stop(); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/ServletInputStreamTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationRequest; import cloud.piranha.core.api.WebApplicationResponse; import jakarta.servlet.ReadListener; import jakarta.servlet.ServletInputStream; import java.io.ByteArrayInputStream; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.Test; /** * The JUnit tests for the ServletInputStream API. * * @author Manfred Riem (mriem@manorrock.com) */ public class ServletInputStreamTest { private WebApplication createWebApplication() { return new DefaultWebApplication(); } private WebApplicationRequest createWebApplicationRequest() { return new DefaultWebApplicationRequest(); } private WebApplicationResponse createWebApplicationResponse() { return new DefaultWebApplicationResponse(); } /** * Test isFinished method. * * @throws Exception when a serious error occurs. */ void testIsFinished() throws Exception { WebApplicationRequest request = createWebApplicationRequest(); ServletInputStream inputStream = request.getInputStream(); assertFalse(inputStream.isFinished()); } /** * Test isReady method. * * @throws Exception when a serious error occurs. */ void testIsReady() throws Exception { WebApplicationRequest request = createWebApplicationRequest(); ServletInputStream inputStream = request.getInputStream(); assertFalse(inputStream.isReady()); } /** * Test read method. * * @throws Exception when a serious error occurs. */ @Test void testRead() throws Exception { WebApplicationRequest request = createWebApplicationRequest(); ServletInputStream inputStream = request.getInputStream(); request.getWebApplicationInputStream() .setInputStream(new ByteArrayInputStream("a".getBytes())); int character = inputStream.read(); assertEquals('a', character); } /** * Test setReadListener method. * * @throws Exception when a serious error occurs. */ @Test void testSetReadListener() throws Exception { WebApplication webApplication = createWebApplication(); WebApplicationRequest request = createWebApplicationRequest(); request.setAsyncSupported(true); request.setWebApplication(webApplication); WebApplicationResponse response = createWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); request.startAsync(); ServletInputStream inputStream = request.getInputStream(); inputStream.setReadListener(new ReadListener() { @Override public void onDataAvailable() throws IOException { throw new UnsupportedOperationException("Not supported yet."); } @Override public void onAllDataRead() throws IOException { throw new UnsupportedOperationException("Not supported yet."); } @Override public void onError(Throwable t) { throw new UnsupportedOperationException("Not supported yet."); } }); assertNotNull(request.getWebApplicationInputStream().getReadListener()); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/ServletRegistrationTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplication; import jakarta.servlet.ServletRegistration; import jakarta.servlet.http.HttpServlet; import java.util.HashMap; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for testing everything related to the ServletRegistration * API. * * @author Manfred Riem (mriem@manorrock.com) */ class ServletRegistrationTest { private WebApplication createWebApplication() { return new DefaultWebApplication(); } /** * Test getClassName method. */ @Test void testGetClassName() { WebApplication webApplication = createWebApplication(); webApplication.addServlet("testGetClassNameServlet", new TestGetClassNameServlet()); ServletRegistration registration = webApplication.getServletRegistration("testGetClassNameServlet"); assertEquals(TestGetClassNameServlet.class.getName(), registration.getClassName()); } /** * Test getInitParameter method. */ @Test void testGetInitParameter() { WebApplication webApplication = createWebApplication(); webApplication.addServlet("testGetInitParameterServlet", new HttpServlet() {}); ServletRegistration registration = webApplication.getServletRegistration("testGetInitParameterServlet"); registration.setInitParameter("name", "value"); assertEquals("value", registration.getInitParameter("name")); } /** * Test getInitParameters method. */ @Test void testGetInitParameters() { WebApplication webApplication = createWebApplication(); webApplication.addServlet("testGetInitParametersServlet", new HttpServlet() {}); ServletRegistration registration = webApplication.getServletRegistration("testGetInitParametersServlet"); assertNotNull(registration.getInitParameters()); } /** * Test getName method. */ @Test void testGetName() { WebApplication webApplication = createWebApplication(); webApplication.addServlet("testGetNameServlet", new HttpServlet() {}); assertNotNull("servlet", webApplication.getServletRegistration("testGetNameServlet").getName()); } /** * Test getRunAsRole method. */ @Test void testGetRunAsRole() { WebApplication webApplication = createWebApplication(); webApplication.addServlet("testGetRunAsRoleServlet", new HttpServlet() {}); ServletRegistration.Dynamic registration = (ServletRegistration.Dynamic) webApplication.getServletRegistration("testGetRunAsRoleServlet"); registration.setRunAsRole("role"); assertNotNull(registration.getRunAsRole()); assertEquals("role", registration.getRunAsRole()); } /** * Test getFilterRegistration method. */ @Test void testGetServletRegistration2() { WebApplication webApplication = createWebApplication(); webApplication.addServlet("testGetServletRegistration2Servlet", "TestGetServletRegistration2"); assertNotNull(webApplication.getServletRegistration("testGetServletRegistration2Servlet")); } /** * Test setInitParameters method. */ @Test void testSetInitParameters() { WebApplication webApplication = createWebApplication(); webApplication.addServlet("testSetInitParametersServlet", new HttpServlet() {}); ServletRegistration registration = webApplication.getServletRegistration("testSetInitParametersServlet"); registration.setInitParameter("name", "value"); assertTrue(registration.setInitParameters(new HashMap<>()).isEmpty()); } /** * Test setInitParameters method. */ @Test void testSetInitParameters2() { WebApplication webApplication = createWebApplication(); webApplication.addServlet("testSetInitParameters2Servlet", new HttpServlet() {}); ServletRegistration registration = webApplication.getServletRegistration("testSetInitParameters2Servlet"); HashMap parameters = new HashMap<>(); parameters.put(null, null); assertThrows(IllegalArgumentException.class, () -> registration.setInitParameters(parameters)); } /** * Test setInitParameters method. */ @Test void testSetInitParameters3() { WebApplication webApplication = createWebApplication(); webApplication.addServlet("testSetInitParameters3Servlet", new HttpServlet() {}); ServletRegistration registration = webApplication.getServletRegistration("testSetInitParameters3Servlet"); HashMap parameters = new HashMap<>(); parameters.put("name", null); assertThrows(IllegalArgumentException.class, () -> registration.setInitParameters(parameters)); } /** * Test setInitParameters method. */ @Test void testSetInitParameters4() { WebApplication webApplication = createWebApplication(); webApplication.addServlet("testSetInitParameters4Servlet", new HttpServlet() {}); ServletRegistration registration = webApplication.getServletRegistration("testSetInitParameters4Servlet"); HashMap parameters = new HashMap<>(); parameters.put("name", "value"); assertTrue(registration.setInitParameters(parameters).isEmpty()); } /** * Test setInitParameters method. */ @Test void testSetInitParameters5() { WebApplication webApplication = createWebApplication(); webApplication.addServlet("testSetInitParameters5Servlet", new HttpServlet() {}); ServletRegistration registration = webApplication.getServletRegistration("testSetInitParameters5Servlet"); HashMap parameters = new HashMap<>(); parameters.put("name", "value"); assertTrue(registration.setInitParameters(parameters).isEmpty()); assertFalse(registration.setInitParameters(parameters).isEmpty()); } /** * Test servlet that is used by testGetClassName. */ public static class TestGetClassNameServlet extends HttpServlet { } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/ServletRequestAttributeListenerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationRequest; import cloud.piranha.core.api.WebApplicationResponse; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequestAttributeEvent; import jakarta.servlet.ServletRequestAttributeListener; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.Test; /** * The JUnit tests for the ServletRequestAttributeListener API. * * @author Manfred Riem (mriem@manorrock.com) */ class ServletRequestAttributeListenerTest { private WebApplication createWebApplication() { return new DefaultWebApplication(); } private WebApplicationRequest createWebApplicationRequest() { return new DefaultWebApplicationRequest(); } private WebApplicationResponse createWebApplicationResponse() { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.getWebApplicationOutputStream().setOutputStream(new ByteArrayOutputStream()); return response; } /** * Test attributeAdded method. * * @throws Exception when a serious error occurs. */ @Test void testAttributeAdded() throws Exception { WebApplication webApplication = createWebApplication(); webApplication.addListener(new ServletRequestAttributeListener() { @Override public void attributeAdded(ServletRequestAttributeEvent event) { event.getServletContext().setAttribute("testAttributeAdded", true); } }); webApplication.addServlet("TestAttributeAddedServlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setAttribute("name", "value"); } }); webApplication.addServletMapping("TestAttributeAddedServlet", "/testAttributeAdded"); webApplication.initialize(); webApplication.start(); WebApplicationRequest request = createWebApplicationRequest(); request.setServletPath("/testAttributeAdded"); request.setWebApplication(webApplication); WebApplicationResponse response = createWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.service(request, response); assertNotNull(webApplication.getAttribute("testAttributeAdded")); } /** * Test attributeRemoved method. * * @throws Exception when a serious error occurs. */ @Test void testAttributeRemoved() throws Exception { WebApplication webApplication = createWebApplication(); webApplication.addListener(new ServletRequestAttributeListener() { @Override public void attributeRemoved(ServletRequestAttributeEvent event) { event.getServletContext().setAttribute("testAttributeRemoved", true); } }); webApplication.addServlet("TestAttributeRemovedServlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setAttribute("name", "value"); request.removeAttribute("name"); } }); webApplication.addServletMapping("TestAttributeRemovedServlet", "/testAttributeRemoved"); webApplication.initialize(); webApplication.start(); WebApplicationRequest request = createWebApplicationRequest(); request.setServletPath("/testAttributeRemoved"); request.setWebApplication(webApplication); WebApplicationResponse response = createWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.service(request, response); assertNotNull(webApplication.getAttribute("testAttributeRemoved")); } /** * Test attributeReplaced method. * * @throws Exception when a serious error occurs. */ @Test void testAttributeReplaced() throws Exception { WebApplication webApplication = createWebApplication(); webApplication.addListener(new ServletRequestAttributeListener() { @Override public void attributeRemoved(ServletRequestAttributeEvent event) { event.getServletContext().setAttribute("testAttributeReplaced", true); } }); webApplication.addServlet("TestAttributeReplacedServlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setAttribute("name", "value"); request.setAttribute("name", "value2"); } }); webApplication.addServletMapping("TestAttributeReplacedServlet", "/testAttributeReplaced"); webApplication.initialize(); webApplication.start(); WebApplicationRequest request = createWebApplicationRequest(); request.setServletPath("/testAttributeReplaced"); request.setWebApplication(webApplication); WebApplicationResponse response = createWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.service(request, response); assertNotNull(webApplication.getAttribute("testAttributeReplaced")); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/ServletTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import cloud.piranha.core.api.WebApplication; /** * The JUnit tests for the Servlet API. * * @author Manfred Riem (mriem@manorrock.com) */ class ServletTest { /** * Stores the web application. */ protected WebApplication webApplication; /** * Setup before testing. * * @throws Exception when a serious error occurs. */ @BeforeEach void setUp() throws Exception { webApplication = new DefaultWebApplication(); } /** * Test service method. * * @throws Exception when a serious error occurs. */ @Test void testService() throws Exception { assertNotNull(webApplication.addServlet("Echo", TestServlet.class)); webApplication.addServletMapping("Echo", "/echo"); webApplication.initialize(); webApplication.start(); TestWebApplicationRequest request = new TestWebApplicationRequest(); request.setServletPath("/echo"); request.setWebApplication(webApplication); TestWebApplicationResponse response = new TestWebApplicationResponse(); response.setBodyOnly(false); response.setWebApplication(webApplication); webApplication.service(request, response); assertTrue(new String(response.getResponseBytes()).contains("200")); assertTrue(new String(response.getResponseBytes()).contains("SUCCESS")); } /** * Test to verify instantiating a Servlet succeeds. */ public static class TestServlet extends HttpServlet { private static final long serialVersionUID = 1L; public TestServlet() { } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.getWriter().println("SUCCESS"); } } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/TestChat1Servlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import java.io.IOException; import java.io.PrintWriter; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.LinkedBlockingQueue; import jakarta.servlet.AsyncContext; import jakarta.servlet.AsyncEvent; import jakarta.servlet.AsyncListener; import jakarta.servlet.ServletConfig; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * A test chat servlet. * * @author Manfred Riem (mriem@manorrock.com) */ public class TestChat1Servlet extends HttpServlet { /** * Stores the async queue. */ private static final ConcurrentLinkedQueue ASYNC_QUEUE = new ConcurrentLinkedQueue<>(); /** * Stores the message queue. */ private static final LinkedBlockingQueue MESSAGE_QUEUE = new LinkedBlockingQueue<>(); /** * Stores the message thread. */ private Thread messageThread = null; /** * Stores serial version UID. */ private static final long serialVersionUID = -2919167206889576860L; /** * Initialize the servlet. * * @param config the servlet config. * @throws ServletException when a Servlet error occurs. */ @Override public void init(ServletConfig config) throws ServletException { Runnable messageRunnable = () -> { boolean done = false; while (!done) { String message; try { message = MESSAGE_QUEUE.take(); ASYNC_QUEUE.stream().forEach((context) -> { try { PrintWriter writer = context.getResponse().getWriter(); writer.println(message); writer.flush(); } catch (IOException exception) { ASYNC_QUEUE.remove(context); } }); } catch (InterruptedException exception) { done = true; } } }; /** * Run the notifier thread. */ messageThread = new Thread(messageRunnable); messageThread.start(); } /** * Get request, which initializes the async processing. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. * @throws ServletException when a Servlet error occurs. */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("text/html"); response.setHeader("Cache-Control", "private"); response.setHeader("Pragma", "no-cache"); PrintWriter writer = response.getWriter(); writer.flush(); final AsyncContext context = request.startAsync(); context.setTimeout(300000); context.addListener(new AsyncListener() { /** * Handle the completion. * * @param event the event. */ @Override public void onComplete(AsyncEvent event) throws IOException { ASYNC_QUEUE.remove(context); } /** * Handle the timeout. * * @param event the event. */ @Override public void onTimeout(AsyncEvent event) throws IOException { ASYNC_QUEUE.remove(context); } /** * Handle an error. * * @param event the event. */ @Override public void onError(AsyncEvent event) throws IOException { ASYNC_QUEUE.remove(context); } /** * Handle starting async processing. * * @param event the event. */ @Override public void onStartAsync(AsyncEvent event) throws IOException { } }); ASYNC_QUEUE.add(context); } /** * Post the action. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. * @throws ServletException when a Servlet error occurs. */ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("text/plain"); response.setHeader("Cache-Control", "private"); response.setHeader("Pragma", "no-cache"); String action = request.getParameter("action"); String name = request.getParameter("name"); if (null != action) { switch (action) { case "login": { String message = name + " has joined."; notify(message); response.getWriter().println("success"); break; } case "post": { String content = request.getParameter("message"); String message = name + " said " + content; notify(message); response.getWriter().println("success"); break; } default: response.sendError(422, "Unable to process"); break; } } } /** * Destroy the servlet. */ @Override public void destroy() { ASYNC_QUEUE.clear(); messageThread.interrupt(); } /** * Notify. * * @param message the message. * @throws IOException when an I/O error occurs. */ private void notify(String message) throws IOException { try { MESSAGE_QUEUE.put(message); } catch (InterruptedException exception) { throw new IOException(exception); } } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/TestChat2Servlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import java.io.IOException; import java.io.PrintWriter; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.LinkedBlockingQueue; import jakarta.servlet.AsyncContext; import jakarta.servlet.AsyncEvent; import jakarta.servlet.AsyncListener; import jakarta.servlet.ServletConfig; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * A test chat servlet. * * @author Manfred Riem (mriem@manorrock.com) */ public class TestChat2Servlet extends HttpServlet { /** * Stores the async queue. */ private static final ConcurrentLinkedQueue ASYNC_QUEUE = new ConcurrentLinkedQueue<>(); /** * Stores the message queue. */ private static final LinkedBlockingQueue MESSAGE_QUEUE = new LinkedBlockingQueue<>(); /** * Stores the message thread. */ private Thread messageThread = null; /** * Stores serial version UID. */ private static final long serialVersionUID = -2919167206889576860L; /** * Initialize the servlet. * * @param config the servlet config. * @throws ServletException when a Servlet error occurs. */ @Override public void init(ServletConfig config) throws ServletException { Runnable messageRunnable = () -> { boolean done = false; while (!done) { String message; try { message = MESSAGE_QUEUE.take(); ASYNC_QUEUE.stream().forEach((context) -> { try { PrintWriter writer = context.getResponse().getWriter(); writer.println(message); writer.flush(); } catch (IOException exception) { ASYNC_QUEUE.remove(context); } }); } catch (InterruptedException exception) { done = true; } } }; /** * Run the notifier thread. */ messageThread = new Thread(messageRunnable); messageThread.start(); } /** * Get request, which initializes the async processing. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. * @throws ServletException when a Servlet error occurs. */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("text/html"); response.setHeader("Cache-Control", "private"); response.setHeader("Pragma", "no-cache"); PrintWriter writer = response.getWriter(); writer.flush(); final AsyncContext context = request.startAsync(request, response); context.setTimeout(300000); context.addListener(new AsyncListener() { /** * Handle the completion. * * @param event the event. */ @Override public void onComplete(AsyncEvent event) throws IOException { ASYNC_QUEUE.remove(context); } /** * Handle the timeout. * * @param event the event. */ @Override public void onTimeout(AsyncEvent event) throws IOException { ASYNC_QUEUE.remove(context); } /** * Handle an error. * * @param event the event. */ @Override public void onError(AsyncEvent event) throws IOException { ASYNC_QUEUE.remove(context); } /** * Handle starting async processing. * * @param event the event. */ @Override public void onStartAsync(AsyncEvent event) throws IOException { } }); ASYNC_QUEUE.add(context); } /** * Post the action. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. * @throws ServletException when a Servlet error occurs. */ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("text/plain"); response.setHeader("Cache-Control", "private"); response.setHeader("Pragma", "no-cache"); String action = request.getParameter("action"); String name = request.getParameter("name"); if (null != action) { switch (action) { case "login": { String message = name + " has joined."; notify(message); response.getWriter().println("success"); break; } case "post": { String content = request.getParameter("message"); String message = name + " said " + content; notify(message); response.getWriter().println("success"); break; } default: response.sendError(422, "Unable to process"); break; } } } /** * Destroy the servlet. */ @Override public void destroy() { ASYNC_QUEUE.clear(); messageThread.interrupt(); } /** * Notify. * * @param message the message. * @throws IOException when an I/O error occurs. */ private void notify(String message) throws IOException { try { MESSAGE_QUEUE.put(message); } catch (InterruptedException exception) { throw new IOException(exception); } } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/TestEcho1Servlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import java.io.IOException; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * A test echo servlet. * * @author Manfred Riem (mriem@manorrock.com) */ public class TestEcho1Servlet extends HttpServlet { /** * Stores the serial version UID. */ private static final long serialVersionUID = 1L; /** * Service the request. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. * @throws ServletException when a servlet error occurs. */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.getWriter().print("ECHO"); response.getWriter().flush(); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/TestEcho2Servlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import java.io.IOException; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * A test echo servlet. * * @author Manfred Riem (mriem@manorrock.com) */ public class TestEcho2Servlet extends HttpServlet { /** * Stores the serial version UID. */ private static final long serialVersionUID = 1L; /** * Service the request. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. * @throws ServletException when a servlet error occurs. */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.getOutputStream().write("ECHO".getBytes()); response.getOutputStream().flush(); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/TestHttpServerRequest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.http.api.HttpServerRequest; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.HashMap; import java.util.Iterator; /** * A Test HttpServerRequest class. * * @author Manfred Riem (mriem@manorrock.com) */ public class TestHttpServerRequest implements HttpServerRequest { @Override public String getHeader(String string) { throw new UnsupportedOperationException("Not supported yet."); } @Override public Iterator getHeaders(String name) { throw new UnsupportedOperationException("Not supported yet."); } @Override public boolean isSecure() { throw new UnsupportedOperationException("Not supported yet."); } /** * Stores the headers. */ private HashMap headers; /** * Stores the input stream. */ private InputStream inputStream; /** * Stores the local address. */ private String localAddress; /** * Stores the local hostname. */ private String localHostname; /** * Stores the local port. */ private int localPort; /** * Stores the method. */ private String method; /** * Stores the query string. */ @SuppressWarnings("unused") private String queryString; /** * Stores the remote address. */ private String remoteAddress; /** * Stores the remote hostname. */ private String remoteHostname; /** * Stores the remote port. */ private int remotePort; /** * Stores the request target. */ private String requestTarget; /** * Constructor. */ public TestHttpServerRequest() { this.headers = new HashMap<>(); this.inputStream = new ByteArrayInputStream(new byte[0]); this.localAddress = "127.0.0.1"; this.localHostname = "localhost"; this.localPort = 8080; this.remoteAddress = "127.0.0.2"; this.remoteHostname = "localhost"; this.remotePort = 18080; this.requestTarget = ""; this.method = "GET"; } /** * {@return the header names} */ @Override public Iterator getHeaderNames() { return headers.keySet().iterator(); } /** * {@return the input stream} */ @Override public InputStream getInputStream() { return inputStream; } /** * {@return the local address} */ @Override public String getLocalAddress() { return localAddress; } /** * {@return the local hostname} */ @Override public String getLocalHostname() { return localHostname; } /** * {@return the local port} */ @Override public int getLocalPort() { return localPort; } /** * {@return the method} */ @Override public String getMethod() { return method; } /** * {@return the remote address} */ @Override public String getRemoteAddress() { return remoteAddress; } /** * {@return the remote hostname} */ @Override public String getRemoteHostname() { return remoteHostname; } /** * {@return the remote port} */ @Override public int getRemotePort() { return remotePort; } /** * {@return the request target} */ @Override public String getRequestTarget() { return requestTarget; } /** * Set the method. * * @param method the method. */ public void setMethod(String method) { this.method = method; } /** * Set the input stream. * * @param inputStream the input stream. */ public void setInputStream(InputStream inputStream) { this.inputStream = inputStream; } /** * Set the local address. * * @param localAddress the local address. */ public void setLocalAddress(String localAddress) { this.localAddress = localAddress; } /** * Set the local hostname. * * @param localHostname the local hostname. */ public void setLocalHostname(String localHostname) { this.localHostname = localHostname; } /** * Set the local port. * * @param localPort the local port. */ public void setLocalPort(int localPort) { this.localPort = localPort; } /** * Set the query string. * * @param queryString the query string. */ public void setQueryString(String queryString) { this.queryString = queryString; } /** * Set the remote address. * * @param remoteAddress the remote address. */ public void setRemoteAddress(String remoteAddress) { this.remoteAddress = remoteAddress; } /** * Set the remote hostname. * * @param remoteHostname the remote hostname. */ public void setRemoteHostname(String remoteHostname) { this.remoteHostname = remoteHostname; } /** * Set the remote port. * * @param remotePort the remote port. */ public void setRemotePort(int remotePort) { this.remotePort = remotePort; } /** * Set the request target. * * @param requestTarget the request target. */ public void setRequestTarget(String requestTarget) { this.requestTarget = requestTarget; } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/TestHttpServerResponse.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.http.api.HttpServerResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; /** * The HttpServerResponse used for testing. * * @author Manfred Riem (mriem@manorrock.com) */ public class TestHttpServerResponse implements HttpServerResponse { /** * Stores the output stream. */ private final ByteArrayOutputStream outputStream; /** * Constructor. */ public TestHttpServerResponse() { this.outputStream = new ByteArrayOutputStream(); } /** * {@return the byte-array output stream} */ public ByteArrayOutputStream getByteArrayOutputStream() { return outputStream; } @Override public void addHeader(String name, String value) { } @Override public String getHeader(String name) { return null; } @Override public OutputStream getOutputStream() { return outputStream; } @Override public void setHeader(String name, String value) { } @Override public void setStatus(int status) { } @Override public void writeHeaders() throws IOException { } @Override public void writeStatusLine() throws IOException { } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/TestIOExceptionServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import java.io.IOException; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * A test I/O exception servlet. * * @author Manfred Riem (mriem@manorrock.com) */ public class TestIOExceptionServlet extends HttpServlet { /** * Stores the serial version UID. */ private static final long serialVersionUID = 1L; /** * Service the request. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. * @throws ServletException when a servlet error occurs. */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { throw new IOException(); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/TestInclude2Servlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import java.io.IOException; import jakarta.servlet.ServletConfig; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * A servlet testing includes (performing the actual include). * * @author Manfred Riem (mriem@manorrock.com) */ public class TestInclude2Servlet extends HttpServlet { /** * Initialize the servlet. * * @param config the servlet config. * @throws ServletException when a Servlet error occurs. */ @Override public void init(ServletConfig config) throws ServletException { } /** * Process GET request. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. * @throws ServletException when a Servlet error occurs. */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.getWriter().print("This was included"); response.getWriter().flush(); } /** * Destroy the servlet. */ @Override public void destroy() { } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/TestInclude3Servlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import java.io.IOException; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletConfig; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * A servlet testing includes (performing the actual include). * * @author Manfred Riem (mriem@manorrock.com) */ public class TestInclude3Servlet extends HttpServlet { /** * Initialize the servlet. * * @param config the servlet config. * @throws ServletException when a Servlet error occurs. */ @Override public void init(ServletConfig config) throws ServletException { } /** * Process GET request. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. * @throws ServletException when a Servlet error occurs. */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { RequestDispatcher rd = request.getServletContext().getRequestDispatcher("/include"); rd.include(request, response); } /** * Destroy the servlet. */ @Override public void destroy() { } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/TestInclude4Servlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import java.io.IOException; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletConfig; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * A servlet testing includes (performing the actual include). * * @author Manfred Riem (mriem@manorrock.com) */ public class TestInclude4Servlet extends HttpServlet { /** * Initialize the servlet. * * @param config the servlet config. * @throws ServletException when a Servlet error occurs. */ @Override public void init(ServletConfig config) throws ServletException { } /** * Process GET request. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. * @throws ServletException when a Servlet error occurs. */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { RequestDispatcher rd = request.getServletContext().getRequestDispatcher("/include"); rd.include(request, response); rd = request.getServletContext().getRequestDispatcher("/include3"); rd.include(request, response); } /** * Destroy the servlet. */ @Override public void destroy() { } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/TestIncludeServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import java.io.IOException; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletConfig; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * A servlet testing includes. * * @author Manfred Riem (mriem@manorrock.com) */ public class TestIncludeServlet extends HttpServlet { /** * Initialize the servlet. * * @param config the servlet config. * @throws ServletException when a Servlet error occurs. */ @Override public void init(ServletConfig config) throws ServletException { } /** * Process GET request. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. * @throws ServletException when a Servlet error occurs. */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { RequestDispatcher rd = request.getServletContext().getRequestDispatcher("/include2"); rd.include(request, response); } /** * Destroy the servlet. */ @Override public void destroy() { } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/TestRuntimeExceptionServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import java.io.IOException; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * A test runtime exception servlet. * * @author Manfred Riem (mriem@manorrock.com) */ public class TestRuntimeExceptionServlet extends HttpServlet { /** * Stores the serial version UID. */ private static final long serialVersionUID = 1L; /** * Service the request. * * @param request the servlet request. * @param response the servlet response. * @throws IOException when an I/O error occurs. * @throws ServletException when a servlet error occurs. */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { throw new RuntimeException(); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/TestSnoopServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import java.io.IOException; import java.io.PrintWriter; import java.util.Arrays; import java.util.Enumeration; import java.util.Locale; import jakarta.servlet.ServletException; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * A test Snoop Servlet. * * @author Manfred Riem (mriem@manorrock.com) */ public class TestSnoopServlet extends HttpServlet { /** * Stores the serial version UID. */ private static final long serialVersionUID = 1L; /** * Processes the request. * * @param request the servlet request. * @param response the servlet response. * @throws IOException when an I/O error occurs. * @throws ServletException when a servlet error occurs. */ protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); out.println(""); out.println(" "); out.println(" Snoop"); out.println(" "); out.println(" "); out.println("

Snoop

"); out.println(" "); out.println(" "); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println("
Attribute Names:" + request.getAttributeNames() + "
Auth Type:" + request.getAuthType() + "
Character Encoding:" + request.getCharacterEncoding() + "
Class:" + request.getClass() + "
Content Length:" + request.getContentLength() + "
Content Type:" + request.getContentType() + "
Context Path:" + request.getContextPath() + "
Cookies:" + Arrays.toString(request.getCookies()) + "
Dispatcher Type:" + request.getDispatcherType() + "
Header Names:" + request.getHeaderNames() + "
Local Address:" + request.getLocalAddr() + "
Local Name:" + request.getLocalName() + "
Local Port:" + request.getLocalPort() + "
Locale:" + request.getLocale() + "
Locales:" + request.getLocales() + "
Method:" + request.getMethod() + "
Parameter Map:" + request.getParameterMap() + "
Parameter Names:" + request.getParameterNames() + "
Path Info:" + request.getPathInfo() + "
Path Translated:" + request.getPathTranslated() + "
Protocol:" + request.getProtocol() + "
Query String:" + request.getQueryString() + "
Remote Address:" + request.getRemoteAddr() + "
Remote Host:" + request.getRemoteHost() + "
Remote Port:" + request.getRemotePort() + "
Remote User:" + request.getRemoteUser() + "
Request URI:" + request.getRequestURI() + "
Request URL:" + request.getRequestURL() + "
Requested Session Id:" + request.getRequestedSessionId() + "
Scheme:" + request.getScheme() + "
Server Name:" + request.getServerName() + "
Server Port:" + request.getServerPort() + "
Servlet Context:" + request.getServletContext() + "
Servlet Path:" + request.getServletPath() + "
Session:" + request.getSession() + "
User Principal:" + request.getUserPrincipal() + "
Is Async Started:" + request.isAsyncStarted() + "
Is Async Supported:" + request.isAsyncSupported() + "
Is Requested Session Id From Cookie:" + request.isRequestedSessionIdFromCookie() + "
Is Requested Session Id From URL:" + request.isRequestedSessionIdFromURL() + "
Is Secure:" + request.isSecure() + "
"); out.println("Attributes"); Enumeration attributeNames = request.getAttributeNames(); out.println(""); while (attributeNames.hasMoreElements()) { String name = attributeNames.nextElement(); out.println(""); } out.println("
" + name + "" + request.getAttribute(name) + "
"); out.println("Cookies"); Cookie[] cookies = request.getCookies(); if (cookies != null) { out.println(""); for (Cookie cookie : cookies) { out.println(""); } out.println("
" + cookie.getName() + "" + cookie.getValue() + "
"); } out.println("Headers"); Enumeration headerNames = request.getHeaderNames(); out.println(""); while (headerNames.hasMoreElements()) { String name = headerNames.nextElement(); out.println(""); } out.println("
" + name + "" + request.getHeader(name) + "
"); out.println("Locales"); Enumeration locales = request.getLocales(); out.println(""); while (locales.hasMoreElements()) { Locale locale = locales.nextElement(); out.println(""); } out.println("Parameters"); Enumeration parameterNames = request.getParameterNames(); out.println("
" + locale + "
"); while (parameterNames.hasMoreElements()) { String name = parameterNames.nextElement(); out.println(""); } out.println("
" + name + "" + Arrays.toString(request.getParameterValues(name)) + "
"); Enumeration initParameterNames = getServletConfig().getInitParameterNames(); out.println("Servlet Config"); out.println("Init Parameters"); out.println(""); while (initParameterNames.hasMoreElements()) { String name = initParameterNames.nextElement(); out.println(""); } out.println("Servlet context: " + getServletConfig().getServletContext()); out.println("Servlet name: " + getServletConfig().getServletName()); out.println(""); out.println(""); } /** * Handles the GET request. * * @param request the servlet request. * @param response the servlet response. * @throws IOException when an I/O error occurs * @throws ServletException when a servlet error occurs */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { processRequest(request, response); } /** * Handles the POST request. * * @param request the servlet request. * @param response the servlet response. * @throws IOException when an I/O error occurs. * @throws ServletException when a servlet error occurs. */ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { processRequest(request, response); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/TestWebApplicationRequest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; /** * A test WebApplicationRequest. * * @author Manfred Riem (mriem@manorrock.com) */ public class TestWebApplicationRequest extends DefaultWebApplicationRequest { } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/TestWebApplicationResponse.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import java.io.ByteArrayOutputStream; /** * A Test WebApplicationResponse. * * @author Manfred Riem (mriem@manorrock.com) */ public class TestWebApplicationResponse extends DefaultWebApplicationResponse { /** * Constructor. */ public TestWebApplicationResponse() { super(); this.bodyOnly = true; } /** * {@return the bytes in the buffer} */ public byte[] getResponseBytes() { ByteArrayOutputStream output = (ByteArrayOutputStream) this.webApplicationOutputStream.getOutputStream(); return output.toByteArray(); } } ================================================ FILE: core/impl/src/test/java/cloud/piranha/core/impl/WriteListenerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.core.impl; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationRequest; import cloud.piranha.core.api.WebApplicationResponse; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.WriteListener; import java.io.ByteArrayOutputStream; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.Test; /** * The JUnit tests for the WriteListener API. * * @author Manfred Riem (mriem@manorrock.com) */ public class WriteListenerTest { private WebApplication createWebApplication() { return new DefaultWebApplication(); } private WebApplicationRequest createWebApplicationRequest() { return new DefaultWebApplicationRequest(); } private WebApplicationResponse createWebApplicationResponse() { return new DefaultWebApplicationResponse(); } /** * Test onWritePossible method. * * @throws Exception when a serious error occurs. */ @Test void testOnWritePossible() throws Exception { WebApplication webApplication = createWebApplication(); WebApplicationRequest request = createWebApplicationRequest(); request.setAsyncSupported(true); request.setWebApplication(webApplication); WebApplicationResponse response = createWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); request.startAsync(); ServletOutputStream outputStream = response.getOutputStream(); response.getWebApplicationOutputStream().setOutputStream(new ByteArrayOutputStream()); outputStream.setWriteListener(new TestOnWritePossibleWriteListener(webApplication)); Thread.sleep(2000); assertNotNull(webApplication.getAttribute("onWritePossible")); } /** * A WriteListener for onWritePossible. */ public static class TestOnWritePossibleWriteListener implements WriteListener { /** * Stores the web application. */ private final WebApplication webApplication; /** * Constructor. * * @param webApplication the web application. */ public TestOnWritePossibleWriteListener(WebApplication webApplication) { this.webApplication = webApplication; } @Override public void onWritePossible() throws IOException { webApplication.setAttribute("onWritePossible", true); } @Override public void onError(Throwable t) { } } } ================================================ FILE: core/impl/src/test/webapp/resourcepaths/catalog/index.html ================================================ ================================================ FILE: core/impl/src/test/webapp/resourcepaths/catalog/offers/books.html ================================================ ================================================ FILE: core/impl/src/test/webapp/resourcepaths/catalog/offers/music.html ================================================ ================================================ FILE: core/impl/src/test/webapp/resourcepaths/catalog/products.html ================================================ ================================================ FILE: core/impl/src/test/webapp/resourcepaths/customer/login.jsp ================================================ ================================================ FILE: core/impl/src/test/webapp/resourcepaths/welcome.html ================================================ ================================================ FILE: core/pom.xml ================================================ 4.0.0 cloud.piranha project 25.4.0-SNAPSHOT cloud.piranha.core project pom Piranha - Core api impl cloud.piranha bom ${project.version} pom import org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test default file:///tmp/piranha/core/ ================================================ FILE: debug/modules.sh ================================================ mvn --also-make dependency:tree | grep "cloud.piranha" | grep -v "cloud.piranha.test" | sed -E 's|.*(cloud)|cloud|' | sed -E 's|>.*||' | awk -F ":" -v OFS=':' '{print $1, $2}' | sed 's/ *$//' | awk '!a[$0]++' | sort ================================================ FILE: debug/pom.xml ================================================ 4.0.0 cloud.piranha project 25.4.0-SNAPSHOT piranha-debug Piranha - Debug This project can be used as entry project in remote debugging, giving one most dependencies of Piranha to step through the source. In the IDE, for instance, Eclipse, go to run - debug configurations - remote java application - project And add this project there. cloud.piranha bom ${project.version} pom import cloud.piranha.arquillian piranha-arquillian-managed ${project.version} compile cloud.piranha.arquillian piranha-arquillian-server ${project.version} compile cloud.piranha.core piranha-core-api ${project.version} compile cloud.piranha.core piranha-core-impl ${project.version} compile cloud.piranha piranha-embedded ${project.version} compile cloud.piranha.extension piranha-extension-annotationscan ${project.version} compile cloud.piranha.extension piranha-extension-annotationscan-classfile ${project.version} compile cloud.piranha.extension piranha-extension-angus ${project.version} compile cloud.piranha.extension piranha-extension-bytesstreamhandler ${project.version} compile cloud.piranha.extension piranha-extension-concurro ${project.version} compile cloud.piranha.extension piranha-extension-coreprofile ${project.version} compile cloud.piranha.extension piranha-extension-datasource ${project.version} compile cloud.piranha.extension piranha-extension-eclipselink ${project.version} compile cloud.piranha.extension piranha-extension-epicyro ${project.version} compile cloud.piranha.extension piranha-extension-exousia ${project.version} compile cloud.piranha.extension piranha-extension-expressly ${project.version} compile cloud.piranha.extension piranha-extension-fileupload ${project.version} compile cloud.piranha.extension piranha-extension-handlestypes ${project.version} compile cloud.piranha.extension piranha-extension-hazelcast ${project.version} compile cloud.piranha.extension piranha-extension-herring ${project.version} compile cloud.piranha.extension piranha-extension-hibernate-validator ${project.version} compile cloud.piranha.extension piranha-extension-jaxb ${project.version} compile cloud.piranha.extension piranha-extension-jersey ${project.version} compile cloud.piranha.extension piranha-extension-jstl ${project.version} compile cloud.piranha.extension piranha-extension-micro ${project.version} compile cloud.piranha.extension piranha-extension-microprofile ${project.version} compile cloud.piranha.extension piranha-extension-naming-cdi ${project.version} compile cloud.piranha.extension piranha-extension-omnifaces-config ${project.version} compile cloud.piranha.extension piranha-extension-omnifaces-microprofile-jwt-auth ${project.version} compile cloud.piranha.extension piranha-extension-omnifaces-omniservices ${project.version} compile cloud.piranha.extension piranha-extension-omnifaces-omniutils ${project.version} compile cloud.piranha.extension piranha-extension-omnifish-omnibeans ${project.version} compile cloud.piranha.extension piranha-extension-omnifish-transact ${project.version} compile cloud.piranha.extension piranha-extension-parsson ${project.version} compile cloud.piranha.extension piranha-extension-platform ${project.version} compile cloud.piranha.extension piranha-extension-policy ${project.version} compile cloud.piranha.extension piranha-extension-scinitializer ${project.version} compile cloud.piranha.extension piranha-extension-security-jakarta ${project.version} compile cloud.piranha.extension piranha-extension-security-servlet ${project.version} compile cloud.piranha.extension piranha-extension-servlet ${project.version} compile cloud.piranha.extension piranha-extension-servletannotations ${project.version} compile cloud.piranha.extension piranha-extension-soteria ${project.version} compile cloud.piranha.extension piranha-extension-tempdir ${project.version} compile cloud.piranha.extension piranha-extension-webprofile ${project.version} compile cloud.piranha.extension piranha-extension-webxml ${project.version} compile cloud.piranha.extension piranha-extension-welcomefile ${project.version} compile cloud.piranha.extension piranha-extension-weld ${project.version} compile cloud.piranha.extension piranha-extension-yasson ${project.version} compile cloud.piranha.extension piranha-extension-wasp ${project.version} compile cloud.piranha.feature piranha-feature-api ${project.version} compile cloud.piranha.feature piranha-feature-crac ${project.version} compile cloud.piranha.feature piranha-feature-exitonstop ${project.version} compile cloud.piranha.feature piranha-feature-http ${project.version} compile cloud.piranha.feature piranha-feature-https ${project.version} compile cloud.piranha.feature piranha-feature-impl ${project.version} compile cloud.piranha.feature piranha-feature-isolatedwebapp ${project.version} compile cloud.piranha.feature piranha-feature-logging ${project.version} compile cloud.piranha.feature piranha-feature-pid ${project.version} compile cloud.piranha.feature piranha-feature-webapp ${project.version} compile cloud.piranha.feature piranha-feature-webapps ${project.version} compile cloud.piranha.http piranha-http-api ${project.version} compile cloud.piranha.http piranha-http-crac ${project.version} compile cloud.piranha.http piranha-http-grizzly ${project.version} compile cloud.piranha.http piranha-http-impl ${project.version} compile cloud.piranha.http piranha-http-jdk ${project.version} compile cloud.piranha.http piranha-http-netty ${project.version} compile cloud.piranha.http piranha-http-tests ${project.version} compile cloud.piranha.http piranha-http-undertow ${project.version} compile cloud.piranha.http piranha-http-virtual ${project.version} compile cloud.piranha.http piranha-http-webapp ${project.version} compile cloud.piranha.maven piranha-maven-plugin ${project.version} compile cloud.piranha piranha-multi ${project.version} compile cloud.piranha.micro piranha-micro-builder ${project.version} compile cloud.piranha.micro piranha-micro-core ${project.version} compile cloud.piranha.micro piranha-micro-loader ${project.version} compile cloud.piranha.resource piranha-resource-api ${project.version} compile cloud.piranha.resource piranha-resource-impl ${project.version} compile cloud.piranha.resource piranha-resource-shrinkwrap ${project.version} compile cloud.piranha piranha-single ${project.version} compile cloud.piranha.spring piranha-embedded-spring-boot-starter ${project.version} compile jakarta.activation jakarta.activation-api compile jakarta.annotation jakarta.annotation-api compile jakarta.authentication jakarta.authentication-api compile jakarta.authorization jakarta.authorization-api compile jakarta.enterprise.concurrent jakarta.enterprise.concurrent-api compile jakarta.enterprise jakarta.enterprise.cdi-api compile jakarta.enterprise jakarta.enterprise.cdi-el-api compile jakarta.platform jakarta.jakartaee-core-api compile jakarta.platform jakarta.jakartaee-web-api compile jakarta.ejb jakarta.ejb-api compile jakarta.el jakarta.el-api compile jakarta.faces jakarta.faces-api compile jakarta.inject jakarta.inject-api compile jakarta.interceptor jakarta.interceptor-api compile jakarta.json.bind jakarta.json.bind-api compile jakarta.json jakarta.json-api compile jakarta.mail jakarta.mail-api compile jakarta.servlet.jsp jakarta.servlet.jsp-api compile jakarta.persistence jakarta.persistence-api compile jakarta.ws.rs jakarta.ws.rs-api compile jakarta.security.enterprise jakarta.security.enterprise-api compile jakarta.servlet jakarta.servlet-api compile jakarta.servlet.jsp.jstl jakarta.servlet.jsp.jstl-api compile jakarta.transaction jakarta.transaction-api compile jakarta.validation jakarta.validation-api compile jakarta.websocket jakarta.websocket-api compile jakarta.websocket jakarta.websocket-client-api compile jakarta.xml.bind jakarta.xml.bind-api compile org.eclipse.microprofile.config microprofile-config-api compile org.eclipse.microprofile.jwt microprofile-jwt-auth-api compile org.apache.maven maven-plugin-api org.apache.maven.plugin-tools maven-plugin-annotations me.alexpanov free-port-finder org.htmlunit htmlunit org.jboss.classfilewriter jboss-classfilewriter io.smallrye jandex org.jboss.shrinkwrap shrinkwrap-impl-base org.jboss.shrinkwrap.descriptors shrinkwrap-descriptors-api-base org.jboss.shrinkwrap.resolver shrinkwrap-resolver-api-maven org.jboss.shrinkwrap.resolver shrinkwrap-resolver-impl-maven org.jboss.shrinkwrap.resolver shrinkwrap-resolver-impl-maven-archive org.jboss.shrinkwrap.resolver shrinkwrap-resolver-spi org.junit.jupiter junit-jupiter org.junit.jupiter junit-jupiter-api org.junit.jupiter junit-jupiter-engine org.junit.jupiter junit-jupiter-params org.junit.platform junit-platform-launcher org.junit.vintage junit-vintage-engine ================================================ FILE: debug/src/main/java/cloud/piranha/debug/Debug.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.debug; /** * Debug class * */ public class Debug { /** * Constructor */ public Debug() { } /** * Main method * @param args args for main method */ public static void main(String[] args) { } } ================================================ FILE: debug/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * The Piranha Debug module. * *

* This module delivers the Debug *

* */ module cloud.piranha.debug { exports cloud.piranha.debug; opens cloud.piranha.debug; } ================================================ FILE: dist/coreprofile/pom.xml ================================================ 4.0.0 cloud.piranha.dist project 25.4.0-SNAPSHOT piranha-dist-coreprofile jar Piranha - Distribution - Core Profile cloud.piranha piranha-single ${project.version} compile cloud.piranha.extension piranha-extension-coreprofile ${project.version} compile cloud.piranha.http piranha-http-grizzly ${project.version} compile jakarta.ws.rs jakarta.ws.rs-api 4.0.0 runtime piranha-dist-coreprofile org.apache.maven.plugins maven-jar-plugin cloud.piranha.dist.coreprofile org.apache.maven.plugins maven-shade-plugin package shade false cloud.piranha.dist.coreprofile.CoreProfilePiranhaMain default file:///tmp/piranha/dist/coreprofile/ org.apache.maven.plugins maven-project-info-reports-plugin ================================================ FILE: dist/coreprofile/src/main/java/cloud/piranha/dist/coreprofile/CoreProfilePiranhaMain.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.dist.coreprofile; import cloud.piranha.extension.coreprofile.CoreProfileExtension; import cloud.piranha.http.grizzly.GrizzlyHttpServer; import cloud.piranha.single.SingleMain; import cloud.piranha.single.SinglePiranhaBuilder; /** * The Main for Piranha Core Profile. * *

* This version of Main changes the following: *

*
    *
  1. extensionClass - set to CoreProfileExtension (unless it was overridden)
  2. *
  3. httpServerClass - set to GrizzlyHttpServer (unless it was overridden)
  4. *
  5. httpsServerClass - set to GrizzlyHttpServer (unless it was overridden)
  6. *
* * @author Manfred Riem (mriem@manorrock.com) */ public class CoreProfilePiranhaMain extends SingleMain { /** * Constructor. */ public CoreProfilePiranhaMain() { super(); } /** * Main method. * * @param arguments the arguments. */ public static void main(String[] arguments) { SinglePiranhaBuilder builder = new CoreProfilePiranhaMain().processArguments(arguments); if (builder != null) { if (builder.getConfiguration().getClass("extensionClass") == null) { builder.extensionClass(CoreProfileExtension.class); } if (builder.getConfiguration().getClass("httpServerClass") == null) { builder.httpServerClass(GrizzlyHttpServer.class.getName()); } if (builder.getConfiguration().getClass("httpsServerClass") == null) { builder.httpsServerClass(GrizzlyHttpServer.class.getName()); } builder.build().start(); } else { showHelp(); } } } ================================================ FILE: dist/coreprofile/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Piranha Core Profile distribution. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.dist.coreprofile { exports cloud.piranha.dist.coreprofile; opens cloud.piranha.dist.coreprofile; requires cloud.piranha.extension.coreprofile; requires cloud.piranha.http.grizzly; requires cloud.piranha.single; } ================================================ FILE: dist/coreprofile/src/site/markdown/create_a_json_rest_service.md ================================================ # Create a JSON REST service If you are looking to create a JSON based REST service with Piranha then consider Piranha Core Profile. It features a runtime ideally suited for REST, JSON and micro services. In 7 steps you will learn how to create the JSON REST service. They are: 1. Create the Maven POM file 1. Add the application class 1. Add the POJO class 1. Add the endpoint 1. Add an integration test 1. Test the application 1. Deploy the application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guides.coreprofile json 24.11.0-SNAPSHOT war Piranha Core Profile - Temperature JSON service 11.0.0-M4 17 5.10.0-M1 3.11.0 3.0.0 3.3.2 23.6.0 UTF-8 jakarta.platform jakarta.jakartaee-core-api ${jakartaee.version} provided org.junit.jupiter junit-jupiter-api ${junit.version} test org.junit.jupiter junit-jupiter-engine ${junit.version} test org.junit.jupiter junit-jupiter-params ${junit.version} test temperature cloud.piranha.maven.plugins piranha-maven-plugin ${piranha.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false ``` ## Add the application class Add the Application class in the `src/main/java` directory, which allows you to set the application path using the @ApplicationPath annotation. ```java package temperature; import jakarta.ws.rs.ApplicationPath; import jakarta.ws.rs.core.Application; @ApplicationPath("") public class TemperatureApplication extends Application { } ``` ## Add the POJO As we are going to be returning JSON in the endpoint we need a POJO to represent the model. Add the POJO class in the `src/main/java` directory. ```java package temperature; public class Temperature { public enum TemperatureScale { CELSIUS, FAHRENHEIT } private TemperatureScale scale; private double temperature; public TemperatureScale getScale() { return scale; } public double getTemperature() { return temperature; } public void setScale(TemperatureScale scale) { this.scale = scale; } public void setTemperature(double temperature) { this.temperature = temperature; } } ``` ## Add the endpoint And the next step is creating the class with the `celsius` and `fahrenheit` endpoints. ```java package temperature; import jakarta.enterprise.context.RequestScoped; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; import static temperature.Temperature.TemperatureScale.CELSIUS; import static temperature.Temperature.TemperatureScale.FAHRENHEIT; @RequestScoped @Path("") public class TemperatureBean { @GET @Produces(APPLICATION_JSON) @Path("/celsius/{celsius}") public Temperature celsius(@PathParam("celsius") double celsius) { Temperature temp = new Temperature(); temp.setScale(CELSIUS); temp.setTemperature(celsius); return temp; } @GET @Produces("application/json") @Path("/fahrenheit/{fahrenheit}") public Temperature fahrenheit(@PathParam("fahrenheit") double fahrenheit) { Temperature temp = new Temperature(); temp.setScale(FAHRENHEIT); temp.setTemperature(fahrenheit); return temp; } } ``` ## Add an integration test As we want to make sure the application gets tested before we release 2 integration tests are added which will be executed as part of the build. We'll add the 2 integration tests to the `src/test/java` directory. ```java package temperature; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; class TemperatureIT { @Test void testCelsius() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:8080/temperature/celsius/18.5")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertEquals("{\"scale\":\"CELSIUS\",\"temperature\":18.5}", response.body()); } @Test void testFahrenheit() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:8080/temperature/fahrenheit/68.0")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertEquals("{\"scale\":\"FAHRENHEIT\",\"temperature\":68.0}", response.body()); } } ``` ## Test the application The application is setup to use JUnit to do integration testing using the Piranha Maven plugin so when you are building the application it will also execute an integration test validating the endpoint works. To build and test the application execute the following command: ```bash mvn install ``` ## Deploy the application To deploy your application you will need 2 pieces. 1. The Piranha Core Profile runtime JAR. 2. The WAR file you just produced. For the WAR file see the `target` directory. For the Piranha Core Profile distribution go to Maven Central. And then the following command line will deploy your application: ```bash java -jar piranha-dist-coreprofile.jar --war-file json.war ``` ## Conclusion As illustrated, creating a JSON based REST service with Piranha Core Profile is pretty straightforward because of the libaries available to you as part of the Jakarta EE Core Profile. ## References 1. [Piranha Core Profile](index.html) 1. [Piranha Maven plugin documentation](../maven-plugin/index.html) [Up](index.html) ================================================ FILE: dist/coreprofile/src/site/markdown/create_a_rest_service.md ================================================ # Create a REST service If you are looking to create a REST service with Piranha then consider Piranha Core Profile. It features a runtime ideally suited for REST and micro services. In 6 steps you will learn how to create the REST service. They are: 1. Create the Maven POM file 1. Add the application class 1. Add the endpoint 1. Add an integration test 1. Test the application 1. Deploy the application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guide.coreprofile rest 1-SNAPSHOT war Create a REST service 11.0.0 21 5.11.3 3.13.0 3.5.2 3.4.0 24.12.0 UTF-8 jakarta.platform jakarta.jakartaee-core-api ${jakartaee.version} provided org.junit.jupiter junit-jupiter-api ${junit.version} test org.junit.jupiter junit-jupiter-engine ${junit.version} test org.junit.jupiter junit-jupiter-params ${junit.version} test rest cloud.piranha.maven piranha-maven-plugin ${piranha.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false ``` ## Add the application class Add the Application class in the `src/main/java` directory, which allows you to set the application path using the @ApplicationPath annotation. ```java package rest; import jakarta.ws.rs.ApplicationPath; import jakarta.ws.rs.core.Application; @ApplicationPath("") public class RestApplication extends Application { } ``` ## Add the endpoint And we are adding a simple 'Hello World' endpoint that is listening on the `/helloworld` path. ```java package rest; import jakarta.enterprise.context.RequestScoped; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @Path("/helloworld") @RequestScoped public class HelloWorldBean { @GET public String helloWorld() { return "Hello World!"; } } ``` ## Add an integration test As we want to make sure the application gets tested before we release an integration test is added which will be executed as part of the build. We'll add the integration test to the `src/test/java` directory. ```java package rest; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; class HelloWorldIT { @Test void testHelloWorld() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:8080/rest/helloworld")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello World!")); } } ``` ## Test the application The application is setup to use JUnit to do integration testing using the Piranha Maven plugin so when you are building the application it will also execute an integration test validating the endpoint works. To build and test the application execute the following command: ```bash mvn install ``` ## Deploy the application To deploy your application you will need 2 pieces. 1. The Piranha Core Profile runtime JAR. 2. The WAR file you just produced. For the WAR file see the `target` directory. For the Piranha Core Profile distribution go to Maven Central. And then the following command line will deploy your application: ```bash java -jar piranha-dist-coreprofile.jar --war-file rest.war ``` ## Conclusion As you can see getting started with Piranha Core Profile does not have to take long. ================================================ FILE: dist/coreprofile/src/site/markdown/debugging_a_rest_service_with_netbeans.md ================================================ # Debugging a REST service with NetBeans If you are looking to debug a REST service with NetBeans then follow along! In 5 steps you will learn how to start debugging the REST service. They are: 1. Create the Maven POM file 1. Add the application class 1. Add the endpoint 1. Add the NetBeans nbactions-default.xml file 1. Debug the application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guides.coreprofile netbeans-debug 24.11.0-SNAPSHOT war Piranha Core Profile - Debugging a REST service with NetBeans 11.0.0-M4 17 5.10.0-M1 3.11.0 3.0.0 3.3.2 23.6.0 UTF-8 jakarta.platform jakarta.jakartaee-core-api ${jakartaee.version} provided org.junit.jupiter junit-jupiter-api ${junit.version} test org.junit.jupiter junit-jupiter-engine ${junit.version} test org.junit.jupiter junit-jupiter-params ${junit.version} test netbeans-debug cloud.piranha.maven.plugins piranha-maven-plugin ${piranha.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false debug cloud.piranha.maven.plugins piranha-maven-plugin ${piranha.version} -Xdebug -agentlib:jdwp=transport=dt_socket,server=n,suspend=n,address=${jpda.address} ``` Note the POM file contains a `debug` profile in the profiles section that we will leverage later on to activate the NetBeans debugging. ## Add the application class Add the Application class in the `src/main/java` directory, which allows you to set the application path using the @ApplicationPath annotation. ```java package rest; import jakarta.ws.rs.ApplicationPath; import jakarta.ws.rs.core.Application; @ApplicationPath("") public class RestApplication extends Application { } ``` ## Add the endpoint And we are adding a simple 'Hello World' endpoint that is listening on the `/helloworld` path. ```java package rest; import jakarta.enterprise.context.RequestScoped; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @Path("/helloworld") @RequestScoped public class HelloWorldBean { @GET public String helloWorld() { return "Hello World!"; } } ``` ## Add the NetBeans nbactions-default.xml To integrate debugging into the NetBeans IDE we'll create the `nbactions-default.xml` file in the root directory of your Maven project. It should contain the content as below. ```xml debug war ear ejb package pre-integration-test true true debug ``` ## Debug the application Open the `HelloWorldBean.java` file and set a breakpoint on the line where the `Hello World` response is returned. Then right click on your project node and select `Debug`. After a short while you will see NetBeans change to the debugger view and the threads of your running application will appear. Then browse to `http://localhost:8080/rest/helloworld/`. Now you'll see it hit your breakpoint. ## Conclusion Setting up debugging for NetBeans requires a little bit of plumbing, but once the Maven profile and the NetBeans nbactions-default.xml file are in place you are set! ## References 1. [Piranha Core Profile](index.html) 1. [Piranha Maven plugin documentation](../maven-plugin/index.html) ================================================ FILE: dist/coreprofile/src/site/markdown/debugging_a_rest_service_with_vscode.md ================================================ # Debugging a REST service with VSCode If you are looking to debug a REST service with VSCode then follow along! In 6 steps you will learn how to start debugging the REST service. They are: 1. Create the Maven POM file 1. Add the application class 1. Add the endpoint 1. Add the VSCode tasks.json file 1. Add the VSCode launch.json file 1. Debug the application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guides.coreprofile vscode-debug 24.11.0-SNAPSHOT war Piranha Core Profile - Debugging a REST service with VSCode 11.0.0-M4 17 5.10.0-M1 3.11.0 3.0.0 3.3.2 23.6.0 UTF-8 jakarta.platform jakarta.jakartaee-core-api ${jakartaee.version} provided org.junit.jupiter junit-jupiter-api ${junit.version} test org.junit.jupiter junit-jupiter-engine ${junit.version} test org.junit.jupiter junit-jupiter-params ${junit.version} test rest cloud.piranha.maven.plugins piranha-maven-plugin ${piranha.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false debug cloud.piranha.maven.plugins piranha-maven-plugin ${piranha.version} -Xdebug -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=9009 ``` Note the POM file contains a `debug` profile in the profiles section that we will leverage later on to activate the VSCode debugging. ## Add the application class Add the Application class in the `src/main/java` directory, which allows you to set the application path using the @ApplicationPath annotation. ```java package rest; import jakarta.ws.rs.ApplicationPath; import jakarta.ws.rs.core.Application; @ApplicationPath("") public class RestApplication extends Application { } ``` ## Add the endpoint And we are adding a simple 'Hello World' endpoint that is listening on the `/helloworld` path. ```java package rest; import jakarta.enterprise.context.RequestScoped; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @Path("/helloworld") @RequestScoped public class HelloWorldBean { @GET public String helloWorld() { return "Hello World!"; } } ``` ## Add the VSCode tasks.json file To integrate debugging into VSCode we'll need a tasks.json file that will trigger the proper Maven goals BEFORE we start the debugger. For that lets first create the `.vscode` directory in the root directory of your Maven project (if it does not exist). And then we will create the `tasks.json` file in the `.vscode` directory. It should contain the content as below. ```json { "version": "2.0.0", "tasks": [ { "label": "Start", "type": "shell", "command": "mvn -Pdebug package pre-integration-test", "isBackground": true, "problemMatcher": "$tsc-watch", } ] } ``` ## Add the VSCode launch.json file The next step is to configure the `launch.json` file to allow it to attach to the REST service after it gets started by means of the `Start` entry in the `tasks.json` file. For that we will create the `tasks.json` file in the `.vscode` directory with the content as below. ```json { "version": "0.2.0", "configurations": [ { "type": "java", "name": "Debug", "preLaunchTask": "Start", "request": "attach", "hostName": "localhost", "port": "9009" } ] } ``` ## Debug the application Open the `HelloWorldBean.java` file and set a breakpoint on the line where the `Hello World` response is returned. Then on your left bar click on the `Debug` icon. And make sure that `Debug` is selected and click the run icon to the left of it. Then browse to `http://localhost:8080/rest/helloworld/`. Now you'll see it hit your breakpoint. ## Conclusion Setting up debugging for VSCode requires a little bit of plumbing, but once the Maven profile, the VSCode `tasks.json` and `launch.json` files are in place it all comes together nicely! ## References 1. [Piranha Core Profile](index.html) 1. [Piranha Maven plugin documentation](../maven-plugin/index.html) ================================================ FILE: dist/coreprofile/src/site/markdown/index.md ================================================ # Piranha Core Profile Piranha Core Profile is our distribution that delivers Jakarta EE Core Profile support. If you are looking to implement micro services then consider this distribution! The following components are available in the Piranha Core Profile distribution: * Jakarta Annotations * Jakarta Contexts and Dependency Injection (CDI Lite section) * Jakarta Dependency Injection * Jakarta Interceptors * Jakarta JSON Processing * Jakarta JSON Binding * Jakarta RESTful Web Services ## Download Piranha Core Profile Piranha Core Profile is available as a JAR file from Maven Central. The download link below will take you to the location on Maven Central where you can download any version of Piranha Core Profle. Select the version by clicking on the directory link and then download the JAR file from there. Download Note the name of the JAR file you want to download should look like piranha-dist-coreprofile-X.Y.Z.jar ## Quickstart Assuming you have downloaded the JAR file as previously indicated running Piranha Core Profile is as simple as: ``` java -jar piranha-dist-coreprofile-X.Y.Z.jar --war-file your-webapplication.war ``` ## Running using Docker If you want to run Piranha Core Profile using Docker we have a base image available for you using Eclipse Temurin as the underlying runtime. See https://github.com/piranhacloud/piranha/pkgs/container/coreprofile for more information and how to pull that image. To use this as a base image you would create your own Dockerfile and add your WAR file using a COPY and change the CMD to point to your WAR file as illustrated below: ```Dockerfile FROM ghcr.io/piranhacloud/coreprofile:latest COPY target/my.war my.war USER root RUN chown -R piranha:piranha /home/piranha USER piranha CMD ["java", "-jar", "piranha-dist-coreprofile.jar", "--war-file", "my.war"] ``` ## Guides 1. [Create a REST service](create_a_rest_service.html) 1. [Create a JSON REST service](create_a_json_rest_service.html) 1. [Debugging a REST service with NetBeans](debugging_a_rest_service_with_netbeans.html) 1. [Debugging a REST service with VSCode](debugging_a_rest_service_with_vscode.html) 1. [Testing with JUnit 5 and Arquillian](testing_with_junit5_and_arquillian.html) 1. [Using Project CRaC](using_project_crac.html) ================================================ FILE: dist/coreprofile/src/site/markdown/testing_with_junit5_and_arquillian.md ================================================ # Testing with JUnit 5 and Arquillian If you are looking to test with JUnit 5 and Arqullien then this guide is for you! In 5 steps you will learn how to do it. They are: 1. Create the Maven POM file 1. Add the application class 1. Add the endpoint 1. Add an integration test 1. Test the application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guides.coreprofile arquillian 24.11.0-SNAPSHOT war Piranha Core Profile - Testing with JUnit 5 and Arquillian 1.7.0.Final 11.0.0-M4 17 5.10.0-M1 3.11.0 3.0.0 3.3.2 23.6.0 UTF-8 jakarta.platform jakarta.jakartaee-core-api ${jakartaee.version} provided cloud.piranha.arquillian piranha-arquillian-jarcontainer ${piranha.version} test org.jboss.arquillian.junit5 arquillian-junit5-container ${arquillian.version} test org.junit.jupiter junit-jupiter ${junit.version} test org.junit.jupiter junit-jupiter-engine ${junit.version} test org.junit.jupiter junit-jupiter-params ${junit.version} test arquillian org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test integration-test integration-test verify org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false ``` ## Add the application class Add the Application class in the `src/main/java` directory, which allows you to set the application path using the @ApplicationPath annotation. ```java package arquillian; import jakarta.ws.rs.ApplicationPath; import jakarta.ws.rs.core.Application; @ApplicationPath("") public class HelloArquillianApplication extends Application { } ``` ## Add the endpoint And we are adding a simple 'Hello World' endpoint that is listening on the `/helloworld` path. ```java package arquillian; import jakarta.enterprise.context.RequestScoped; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @Path("/helloarquillian") @RequestScoped public class HelloArquillianBean { @GET public String helloArquillian() { return "Hello Arquillian!"; } } ``` ## Add an integration test As we want to make sure the application gets tested before we release an integration test is added which will be executed as part of the build. We'll add the integration test to the `src/test/java` directory. ```java package helloarquillian; import arquillian.HelloArquillianApplication; import arquillian.HelloArquillianBean; import java.net.URI; import java.net.URL; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.RunAsClient; import org.jboss.arquillian.junit5.ArquillianExtension; import org.jboss.arquillian.test.api.ArquillianResource; import static org.jboss.shrinkwrap.api.ShrinkWrap.create; import org.jboss.shrinkwrap.api.spec.WebArchive; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @ExtendWith(ArquillianExtension.class) public class HelloArquillianIT { @ArquillianResource private URL baseUrl; @Deployment(testable = false) public static WebArchive createDeployment() { return create(WebArchive.class) .addClass(HelloArquillianApplication.class) .addClass(HelloArquillianBean.class); } @Test @RunAsClient public void testHelloArquillian() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI(baseUrl + "helloarquillian")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello Arquillian!")); } } ``` ## Test the application The application is now setup to use JUnit 5 and Arquillian to do the integration testing. So when you are building the application it will also execute an integration test validating the endpoint works. To build and test the application execute the following command: ```bash mvn install ``` ## Conclusion As you can see using JUnit 5 and Arquillian is pretty straightforward! ## References 1. [Piranha Core Profile](index.html) 1. [Arquillian](https://arquillian.org/) [Up](index.html) ================================================ FILE: dist/coreprofile/src/site/markdown/using_project_crac.md ================================================ # Using Project CRaC If you are looking to get CRaC-ing with Piranha Servlet you can follow along! _Make sure you are using a CRaC enabled JDK available from https://github.com/CRaC/openjdk-builds/releases or from any vendor that delivers a CRaC capable OpenJDK distribution._ In 8 steps you will learn how to deploy CRaC with Piranha Servlet. They are: 1. Create the Maven POM file 1. Add the application class 1. Add the endpoint 1. Add an integration test 1. Test the application 1. Start the application 1. Create the checkpoint 1. Restart the application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guides.crac crac 24.11.0-SNAPSHOT war Piranha Core Profile on Project CRaC 11.0.0-M4 5.11.0-M2 24.7.0 21 UTF-8 3.13.0 3.3.1 3.4.0 jakarta.platform jakarta.jakartaee-core-api ${jakartaee.version} provided org.junit.jupiter junit-jupiter-api ${junit.version} test org.junit.jupiter junit-jupiter-engine ${junit.version} test org.junit.jupiter junit-jupiter-params ${junit.version} test crac cloud.piranha.maven.plugins piranha-maven-plugin ${piranha.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false ``` ## Add the application class Add the Application class in the `src/main/java` directory, which allows you to set the application path using the @ApplicationPath annotation. ```java package rest; import jakarta.ws.rs.ApplicationPath; import jakarta.ws.rs.core.Application; @ApplicationPath("") public class RestApplication extends Application { } ``` ## Add the endpoint And we are adding a simple 'Hello World' endpoint that is listening on the `/helloworld` path. ```java package rest; import jakarta.enterprise.context.RequestScoped; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @Path("/helloworld") @RequestScoped public class HelloWorldBean { @GET public String helloWorld() { return "Hello World!"; } } ``` ## Add an integration test As we want to make sure the application gets tested before we release an integration test is added which will be executed as part of the build. We'll add the integration test to the `src/test/java` directory. ```java package rest; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; class HelloWorldIT { @Test void testHelloWorld() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:8080/crac/helloworld")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello World!")); } } ``` ## Test the application The application is setup to use JUnit to do integration testing using the Piranha Maven plugin so when you are building the application it will also execute an integration test validating the endpoint works. To build and test the application execute the following command: ```bash mvn install ``` ## Deploy the application To deploy your application you will need 2 pieces. 1. The Piranha Core Profile runtime JAR. 2. The WAR file you just produced. For the WAR file see the `target` directory. For the Piranha Core Profile distribution go to Maven Central. And then the following command line will deploy your application: ```bash java -XX:CRaCCheckpointTo=cr -jar piranha-dist-coreprofile.jar --enable-crac --write-pid --war-file crac.war & ``` ## Create the checkpoint You have your application currently running so now it is time to create a checkpoint. To ask the JVM to create a checkpoint use the command line below: ```bash jcmd piranha-dist-coreprofile.jar JDK.checkpoint ``` The application will exit and the chechkpoint files are in the `cr` directory. ## Restart the application To restart the application for the checkpoint you can use the following command line: ```bash java -XX:CRaCRestoreFrom=cr ``` ## Conclusion As you can see using CRaC with Piranha Core Profile is matter of the right JDK and the right command line switch. ## References 1. [CRaC Project](https://wiki.openjdk.org/display/crac/Main) ================================================ FILE: dist/coreprofile/src/site/site.xml ================================================ ================================================ FILE: dist/isolated/pom.xml ================================================ 4.0.0 cloud.piranha.dist project 25.4.0-SNAPSHOT piranha-dist-isolated jar Piranha - Distribution - Isolated The isolated version of Piranha. cloud.piranha piranha-embedded ${project.version} compile cloud.piranha.core piranha-core-impl ${project.version} compile cloud.piranha.extension piranha-extension-servlet ${project.version} compile cloud.piranha.feature piranha-feature-exitonstop ${project.version} compile cloud.piranha.feature piranha-feature-http ${project.version} compile cloud.piranha.feature piranha-feature-https ${project.version} compile cloud.piranha.feature piranha-feature-logging ${project.version} compile cloud.piranha.http piranha-http-webapp ${project.version} compile cloud.piranha.micro piranha-micro-builder ${project.version} compile cloud.piranha.micro piranha-micro-loader ${project.version} compile cloud.piranha.resource piranha-resource-impl ${project.version} compile cloud.piranha.resource piranha-resource-shrinkwrap ${project.version} compile true io.smallrye jandex compile true org.jboss.shrinkwrap.descriptors shrinkwrap-descriptors-api-base compile true org.jboss.shrinkwrap.resolver shrinkwrap-resolver-api-maven compile true org.jboss.shrinkwrap.resolver shrinkwrap-resolver-impl-maven compile true org.jboss.shrinkwrap.resolver shrinkwrap-resolver-impl-maven-archive compile true org.jboss.shrinkwrap.resolver shrinkwrap-resolver-spi compile true org.junit.jupiter junit-jupiter-api test piranha-dist-isolated org.apache.maven.plugins maven-assembly-plugin package single false src/main/assembly/zip.xml org.apache.maven.plugins maven-failsafe-plugin integration-test verify org.apache.maven.plugins maven-jar-plugin true cloud.piranha.dist.isolated.IsolatedPiranha default file:///tmp/piranha/dist/isolated/ docker io.fabric8 docker-maven-plugin isolated ghcr.io/piranhacloud/isolated:%l linux/amd64 linux/arm64 ${basedir} src/main/docker/Dockerfile build install build push deploy push org.apache.maven.plugins maven-javadoc-plugin true org.sonatype.plugins nexus-staging-maven-plugin true true ================================================ FILE: dist/isolated/src/main/assembly/zip.xml ================================================ zip piranha zip tar.gz ${project.basedir}/src/main/zip ${project.build.directory}/webapps webapps ${project.build.directory}/piranha-dist-isolated.jar lib ${project.groupId}:${project.artifactId}:jar:* lib true ================================================ FILE: dist/isolated/src/main/docker/Dockerfile ================================================ FROM eclipse-temurin:21 RUN useradd -m piranha ADD target/piranha-dist-isolated.tar.gz /home/piranha/ RUN chown -R piranha:piranha /home/piranha WORKDIR /home/piranha/piranha/bin USER piranha CMD ["/bin/bash", "./run.sh"] ================================================ FILE: dist/isolated/src/main/java/cloud/piranha/dist/isolated/IsolatedPiranha.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.dist.isolated; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.lang.System.Logger.Level; import java.lang.System.Logger; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.importer.ZipImporter; import org.jboss.shrinkwrap.api.spec.WebArchive; import cloud.piranha.core.api.Piranha; import cloud.piranha.core.api.PiranhaConfiguration; import cloud.piranha.core.impl.DefaultPiranhaConfiguration; import cloud.piranha.feature.api.FeatureManager; import cloud.piranha.feature.exitonstop.ExitOnStopFeature; import cloud.piranha.feature.http.HttpFeature; import cloud.piranha.feature.https.HttpsFeature; import cloud.piranha.feature.impl.DefaultFeatureManager; import cloud.piranha.feature.logging.LoggingFeature; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.webapp.HttpWebApplicationServer; import cloud.piranha.micro.builder.MicroWebApplication; import cloud.piranha.micro.loader.MicroConfiguration; import cloud.piranha.micro.loader.MicroOuterDeployer; import static java.lang.System.Logger.Level.INFO; import static java.lang.System.Logger.Level.WARNING; import java.nio.file.Files; /** * The Isolated of Piranha. * * @author Manfred Riem (mriem@manorrock.com) * @author Arjan Tijms */ public class IsolatedPiranha implements Piranha, Runnable { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(IsolatedPiranha.class.getName()); /** * Stores the one and only instance of the server. */ private static IsolatedPiranha theOneAndOnlyInstance; /** * Stores the configuration. */ private final PiranhaConfiguration configuration; /** * Stores the feature manager. */ private FeatureManager featureManager; /** * Stores the HTTP feature. */ private HttpFeature httpFeature; /** * Stores the HTTPS feature. */ private HttpsFeature httpsFeature; /** * Stores the HTTP web application server. */ private HttpWebApplicationServer webApplicationServer; /** * Constructor. */ public IsolatedPiranha() { configuration = new DefaultPiranhaConfiguration(); configuration.setInteger("httpPort", 8080); configuration.setInteger("httpsPort", -1); featureManager = new DefaultFeatureManager(); } @Override public PiranhaConfiguration getConfiguration() { return configuration; } /** * Get the only instance. * * @return the instance. */ public static IsolatedPiranha get() { return theOneAndOnlyInstance; } /** * Main method. * * @param arguments the arguments. */ public static void main(String[] arguments) { theOneAndOnlyInstance = new IsolatedPiranha(); theOneAndOnlyInstance.configuration.setBoolean("exitOnStop", true); theOneAndOnlyInstance.processArguments(arguments); theOneAndOnlyInstance.run(); } /** * Process the arguments. * * @param arguments the arguments. */ private void processArguments(String[] arguments) { if (arguments != null) { for (int i = 0; i < arguments.length; i++) { if (arguments[i].equals("--http-port")) { configuration.setInteger("httpPort", Integer.valueOf(arguments[i + 1])); } if (arguments[i].equals("--http-server-class")) { configuration.setString("httpServerClass", arguments[i + 1]); } if (arguments[i].equals("--https-port")) { configuration.setInteger("httpsPort", Integer.valueOf(arguments[i + 1])); } if (arguments[i].equals("--https-server-class")) { configuration.setString("httpsServerClass", arguments[i + 1]); } if (arguments[i].equals("--logging-level")) { configuration.setString("loggingLevel", arguments[i + 1]); } } } } /** * Start method. */ @Override public void run() { long startTime = System.currentTimeMillis(); LoggingFeature loggingFeature = new LoggingFeature(); featureManager.addFeature(loggingFeature); loggingFeature.setLevel(configuration.getString("loggingLevel")); loggingFeature.init(); loggingFeature.start(); LOGGER.log(INFO, () -> "Starting Piranha"); webApplicationServer = new HttpWebApplicationServer(); webApplicationServer.start(); File[] webapps = new File("webapps").listFiles(); if (webapps != null) { if (webapps.length != 0) { // Limit threads used by Weld, since default is Runtime.getRuntime().availableProcessors(), which is per deployment. int threadsPerApp = Math.max(2, Runtime.getRuntime().availableProcessors() / webapps.length); System.setProperty("org.jboss.weld.executor.threadPoolSize", threadsPerApp + ""); } File deployingFile = createDeployingFile(); Arrays.stream(webapps) .parallel() .filter(warFile -> warFile.getName().toLowerCase().endsWith(".war")) .forEach(warFile -> deploy(warFile, webApplicationServer)); try { Files.delete(deployingFile.toPath()); } catch (IOException ioe) { LOGGER.log(WARNING, "Unable to delete deploying file", ioe); } } HttpServer httpServer = null; /* * Construct, initialize and start HTTP endpoint (if applicable). */ if (configuration.getInteger("httpPort") > 0) { httpFeature = new HttpFeature(); httpFeature.setHttpServerClass(configuration.getString("httpServerClass")); httpFeature.setPort(configuration.getInteger("httpPort")); httpFeature.init(); httpFeature.getHttpServer().setHttpServerProcessor(webApplicationServer); httpFeature.start(); httpServer = httpFeature.getHttpServer(); } /* * Construct, initialize and start HTTPS endpoint (if applicable). */ if (configuration.getInteger("httpsPort") > 0) { httpsFeature = new HttpsFeature(); httpsFeature.setHttpsServerClass(configuration.getString("httpsServerClass")); httpsFeature.setPort(configuration.getInteger("httpsPort")); httpsFeature.init(); httpsFeature.getHttpsServer().setHttpServerProcessor(webApplicationServer); httpsFeature.start(); if (httpServer == null) { httpServer = httpsFeature.getHttpsServer(); } } if (configuration.getBoolean("exitOnStop", false)) { ExitOnStopFeature exitOnStopFeature = new ExitOnStopFeature(); featureManager.addFeature(exitOnStopFeature); exitOnStopFeature.init(); exitOnStopFeature.start(); } long finishTime = System.currentTimeMillis(); LOGGER.log(INFO, "Started Piranha"); LOGGER.log(INFO, "It took {0} milliseconds", finishTime - startTime); File startedFile = createStartedFile(); File pidFile = new File("tmp/piranha.pid"); if (httpServer != null) { while (httpServer.isRunning()) { try { Thread.sleep(2000); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } if (!pidFile.exists()) { webApplicationServer.stop(); httpServer.stop(); try { Files.delete(startedFile.toPath()); } catch (IOException ioe) { LOGGER.log(WARNING, "Unable to delete PID file", ioe); } } } } finishTime = System.currentTimeMillis(); LOGGER.log(INFO, "Stopped Piranha"); LOGGER.log(INFO, "We ran for {0} milliseconds", finishTime - startTime); featureManager.stop(); } private void deploy(File warFile, HttpWebApplicationServer webApplicationServer) { String contextPath = getContextPath(warFile); MicroConfiguration configuration = new MicroConfiguration(); configuration.setContextPath(contextPath); configuration.setHttpStart(false); try { MicroWebApplication microWebApplication = new MicroWebApplication(); microWebApplication.setContextPath(contextPath); microWebApplication.setDeployedApplication( new MicroOuterDeployer(configuration.postConstruct()) .deploy(ShrinkWrap.create(ZipImporter.class, warFile.getName()).importFrom(warFile).as(WebArchive.class)) .getDeployedApplication()); webApplicationServer.addWebApplication(microWebApplication); } catch (Exception e) { LOGGER.log(Level.ERROR, () -> "Failed to initialize app " + contextPath, e); } } private String getContextPath(File warFile) { String contextPath = warFile.getName().substring(0, warFile.getName().length() - 4); if (contextPath.equalsIgnoreCase("ROOT")) { contextPath = ""; } else if (!contextPath.startsWith("/")) { contextPath = "/" + contextPath; } return contextPath; } private File createDeployingFile() { File deployingFile = new File("webapps/deploying"); try { if (!deployingFile.createNewFile()) { LOGGER.log(Level.WARNING, "Unable to create deploying file"); } } catch (IOException e) { LOGGER.log(Level.WARNING, "I/O error occurred creating deploying file", e); } return deployingFile; } private File createStartedFile() { File startedFile = new File("webapps/started"); try { if (!startedFile.createNewFile()) { LOGGER.log(Level.WARNING, "Unable to create started file"); } } catch (IOException e) { LOGGER.log(Level.WARNING, "I/O error occurred creating started file", e); } return startedFile; } } ================================================ FILE: dist/isolated/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ import cloud.piranha.http.api.HttpServer; /** * This module delivers the Piranha Isolated distribution. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.dist.isolated { opens cloud.piranha.dist.isolated; exports cloud.piranha.dist.isolated; requires transitive cloud.piranha.core.api; requires cloud.piranha.embedded; requires cloud.piranha.extension.servlet; requires cloud.piranha.feature.exitonstop; requires cloud.piranha.feature.http; requires cloud.piranha.feature.https; requires cloud.piranha.feature.logging; requires cloud.piranha.http.webapp; requires cloud.piranha.micro.builder; requires cloud.piranha.micro.loader; requires cloud.piranha.resource.shrinkwrap; requires org.jboss.jandex; requires shrinkwrap.api; requires shrinkwrap.resolver.api.maven; requires java.logging; uses HttpServer; } ================================================ FILE: dist/isolated/src/main/zip/etc/logging.properties ================================================ # # Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # # Logging handlers, by default only the ConsoleHandler is enabled. If you want to # add other handlers, see the java.util.logging documentation for more # information. # handlers = java.util.logging.ConsoleHandler # # Global log level # .level = INFO # # The configuration for the ConsoleHandler # # 1. The log level is set to INFO # 2. The formatter used is the SimpleFormatter # # IMPORTANT NOTE # # A Handler (in this case the ConsoleHandler) will only log up up to the level # specified for its Handler. So be default only INFO messages and higher will # show up in the log. E.g if you want FINEST messages to show for a particular # Logger you will have to set the level of the Handler to FINEST as well. # # java.util.logging.ConsoleHandler.level = INFO java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter # # Note Piranha modules use package level based logging. So if you want to limit # the amount of logs you want to see you can do so on a package level basis. # ================================================ FILE: dist/isolated/src/main/zip/tmp/README ================================================ README ------ This directory is used for temporary files by Piranha. If the server is NOT running you can safely delete the contents of the directory, but do NOT delete the directory itself. ================================================ FILE: dist/isolated/src/main/zip/webapps/README ================================================ README ------ This directory is used for your web applications. If a file ends with the .war extension, or it is a sub directory it will be considered a web application, anything else will be ignored. ================================================ FILE: dist/isolated/src/test/java/cloud/piranha/dist/isolated/IsolatedPiranhaIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.dist.isolated; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import org.junit.jupiter.api.Test; /** * The JUnit integration tests for ServerPiranha class. * * @author Manfred Riem (mriem@manorrock.com) */ class IsolatedPiranhaIT { /** * Extract the zip input stream. * * @param zipInput the zip input stream. * @param filePath the file path. * @throws IOException when an I/O error occurs. */ private void extractZipInputStream(ZipInputStream zipInput, String filePath) throws IOException { try (BufferedOutputStream bufferOutput = new BufferedOutputStream(new FileOutputStream(filePath))) { byte[] bytesIn = new byte[8192]; int read; while ((read = zipInput.read(bytesIn)) != -1) { bufferOutput.write(bytesIn, 0, read); } } } /** * Extract the Server zip file. */ private void extractServer(File zipFile) { try (ZipInputStream zipInput = new ZipInputStream(new FileInputStream(zipFile))) { ZipEntry entry = zipInput.getNextEntry(); while (entry != null) { String filePath = "target" + File.separatorChar + entry.getName(); if (!entry.isDirectory()) { File file = new File(filePath); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } extractZipInputStream(zipInput, filePath); } zipInput.closeEntry(); entry = zipInput.getNextEntry(); } } catch (IOException ioe) { } } /** * Test run method. * * @throws Exception when a serious error occurs. */ @Test void testRun() throws Exception { extractServer(new File("target/piranha-dist-isolated.zip")); ProcessBuilder builder = new ProcessBuilder(); Process process; if (System.getProperty("os.name").toLowerCase().contains("windows")) { process = builder. directory(new File("target/piranha/bin")). command("cmd", "/c", "start.cmd"). start(); } else { process = builder. directory(new File("target/piranha/bin")). command("sh", "./start.sh"). start(); } process.waitFor(15, TimeUnit.SECONDS); if (System.getProperty("os.name").toLowerCase().contains("windows")) { process = builder. directory(new File("target/piranha/bin")). command("cmd", "/c", "stop.cmd"). start(); } else { process = builder. directory(new File("target/piranha/bin")). command("sh", "./stop.sh"). start(); } process.waitFor(15, TimeUnit.SECONDS); process.destroy(); } } ================================================ FILE: dist/micro/pom.xml ================================================ 4.0.0 cloud.piranha.dist project 25.4.0-SNAPSHOT piranha-dist-micro jar Piranha - Distribution - Micro cloud.piranha.core piranha-core-impl ${project.version} true cloud.piranha.micro piranha-micro-loader ${project.version} cloud.piranha.resource piranha-resource-impl ${project.version} true cloud.piranha.resource piranha-resource-shrinkwrap ${project.version} true org.jboss.shrinkwrap.descriptors shrinkwrap-descriptors-api-base true org.jboss.shrinkwrap.resolver shrinkwrap-resolver-api-maven true org.jboss.shrinkwrap.resolver shrinkwrap-resolver-spi true org.jboss.shrinkwrap.resolver shrinkwrap-resolver-impl-maven true org.jboss.shrinkwrap.resolver shrinkwrap-resolver-impl-maven-archive true io.smallrye jandex true org.junit.jupiter junit-jupiter-api test cloud.piranha.micro piranha-micro-core ${project.version} provided cloud.piranha.extension piranha-extension-micro ${project.version} provided piranha-dist-micro org.codehaus.mojo build-helper-maven-plugin reserve-network-port reserve-network-port pre-integration-test httpPort org.apache.maven.plugins maven-compiler-plugin default-compile module-info.java patch-compile compile compile --patch-module=shrinkwrap.resolver.api.maven=${settings.localRepository}/org/jboss/shrinkwrap/resolver/shrinkwrap-resolver-api/3.3.0/shrinkwrap-resolver-api-3.3.0.jar org.apache.maven.plugins maven-jar-plugin cloud.piranha.dist.micro org.apache.maven.plugins maven-failsafe-plugin integration-test verify ${httpPort} org.apache.maven.plugins maven-shade-plugin package shade false org.jboss.shrinkwrap:shrinkwrap-impl-base ** org.jboss.shrinkwrap.resolver:shrinkwrap-resolver-spi ** cloud.piranha.dist.micro.MicroBootstrap org.moditect moditect-maven-plugin add-module-infos package add-module-info true cloud.piranha.dist piranha-dist-micro ${project.version} ${project.build.sourceDirectory}/module-info.java default file:///tmp/piranha/dist/micro/ org.apache.maven.plugins maven-project-info-reports-plugin 3.9.0 docker io.fabric8 docker-maven-plugin micro ghcr.io/piranhacloud/micro:%l linux/amd64 linux/arm64 ${basedir} src/main/docker/Dockerfile build install build push deploy push org.apache.maven.plugins maven-javadoc-plugin true org.sonatype.plugins nexus-staging-maven-plugin true true ================================================ FILE: dist/micro/src/main/docker/Dockerfile ================================================ FROM eclipse-temurin:21 RUN useradd -m piranha ADD target/piranha-dist-micro.jar /home/piranha WORKDIR /home/piranha RUN chown piranha:piranha piranha-dist-micro.jar USER piranha CMD ["java", "-jar", "piranha-dist-micro.jar"] ================================================ FILE: dist/micro/src/main/java/cloud/piranha/dist/micro/MicroBootstrap.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.dist.micro; import cloud.piranha.core.api.PiranhaConfiguration; import cloud.piranha.core.impl.DefaultPiranhaConfiguration; import java.io.File; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.importer.ExplodedImporter; import org.jboss.shrinkwrap.api.importer.ZipImporter; import org.jboss.shrinkwrap.api.spec.WebArchive; import cloud.piranha.micro.loader.MicroConfiguration; import cloud.piranha.micro.loader.MicroOuterDeployer; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import static java.lang.System.Logger.Level.WARNING; /** * The micro version of Piranha. * * @author Manfred Riem (mriem@manorrock.com) * @author Arjan Tijms */ public class MicroBootstrap implements Runnable { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(MicroBootstrap.class.getName()); /** * Stores the configuration. */ private final PiranhaConfiguration configuration; /** * Stores the HTTP server implementation. */ private String httpServer = "impl"; /** * Stores the WAR file. */ private Archive archive; /** * Stores the outer deployer. */ private MicroOuterDeployer outerDeployer; /** * Constructor. */ public MicroBootstrap() { configuration = new DefaultPiranhaConfiguration(); configuration.setString("contextPath", "ROOT"); configuration.setInteger("httpPort", 8080); } /** * Main method. * * @param arguments the arguments. */ public static void main(String[] arguments) { MicroBootstrap runner = new MicroBootstrap(); runner.configure(arguments); runner.run(); while (true) { try { Thread.sleep(2000); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } } /** * Configure. * * @param arguments the arguments. */ public void configure(String[] arguments) { File warFile = null; File explodedDir = null; if (arguments.length > 0) { for (int i = 0; i < arguments.length; i++) { if (arguments[i].equals("--context-path")) { configuration.setString("contextPath", arguments[i + 1]); } if (arguments[i].equals("--http-port")) { configuration.setInteger("httpPort", Integer.valueOf(arguments[i + 1])); } if (arguments[i].equals("--war-file")) { warFile = new File(arguments[i + 1]); } if (arguments[i].equals("--webapp")) { explodedDir = new File(arguments[i + 1]); } if (arguments[i].equals("--http")) { httpServer = arguments[i + 1]; } if (arguments[i].equals("--ssl")) { System.setProperty("piranha.http.ssl", "true"); } if (arguments[i].equals("--write-pid")) { configuration.setBoolean("writePid", Boolean.TRUE); } } } if (warFile != null) { archive = ShrinkWrap.create(ZipImporter.class, warFile.getName()).importFrom(warFile).as(WebArchive.class); } else if (explodedDir != null) { archive = ShrinkWrap.create(ExplodedImporter.class, explodedDir.getName()).importDirectory(explodedDir).as(WebArchive.class); } else { archive = ShrinkWrap.create(WebArchive.class); } } /** * Start method. */ @Override public void run() { MicroConfiguration microConfiguration = new MicroConfiguration(); microConfiguration.setHttpServer(httpServer); microConfiguration.setContextPath(configuration.getString("contextPath")); microConfiguration.setPort(configuration.getInteger("httpPort")); outerDeployer = new MicroOuterDeployer(microConfiguration.postConstruct()); outerDeployer.deploy(archive); if (configuration.getBoolean("writePid", false)) { File pidFile = new File("tmp", "piranha.pid"); if (!pidFile.getParentFile().exists() && !pidFile.getParentFile().mkdirs()) { LOGGER.log(WARNING, "Unable to create tmp directory for PID file"); } try (PrintWriter writer = new PrintWriter(new FileWriter(pidFile))) { writer.print(ProcessHandle.current().pid()); writer.flush(); } catch (IOException ioe) { LOGGER.log(WARNING, "Unable to write PID file", ioe); } } } /** * Stop method. */ public void stop() { if (outerDeployer != null) { outerDeployer.stop(); } } } ================================================ FILE: dist/micro/src/main/java/cloud/piranha/dist/micro/package-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** *

* This package delivers you with a Servlet container that hosts only a single * application. *

* *

Architecture diagram

* *

* The image below illustrates how the request and response handling is done by * Piranha Micro. When a request comes in to the HTTP server it dispatches it to * the WebApplicationServer which in turn * dispatches it to the WebApplication which then in turn uses * WebApplicationRequestMapper to determine * which FilterChain needs to process the incoming request and it dispatches to * it. *

* *

* Request and response handling *

* *

How do I use Piranha Micro?

* *

* See our documentation for more * information. *

* * @author Manfred Riem (mriem@manorrock.com) */ package cloud.piranha.dist.micro; ================================================ FILE: dist/micro/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Piranha Micro distribution. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.dist.micro { exports cloud.piranha.dist.micro; opens cloud.piranha.dist.micro; requires cloud.piranha.core.impl; requires cloud.piranha.resource.impl; requires cloud.piranha.resource.shrinkwrap; requires java.logging; requires org.jboss.jandex; requires shrinkwrap.api; requires shrinkwrap.resolver.api.maven; // Tests requires static java.net.http; requires cloud.piranha.micro.loader; } ================================================ FILE: dist/micro/src/site/markdown/create_a_hello_world_web_application.md ================================================ # Create a Hello World web application If you are looking to create a simple Hello World web application with Piranha Micro to get started then you have come to the right place! In 6 steps you will learn how to create the web application. They are: 1. Create the Maven POM file 1. Add the HelloWorldServlet.java file 1. Add the web.xml file 1. Add an integration test 1. Test the application 1. Deploy the application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guides.micro helloworld 24.11.0-SNAPSHOT war Piranha Micro - Create a Hello World application 11.0.0-M4 5.9.1 21 micro 24.9.0 UTF-8 3.10.1 3.0.0-M7 3.3.2 piranha-micro-helloworld cloud.piranha.maven piranha-maven-plugin ${piranha.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop ${piranha.distribution} org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false jakarta.platform jakarta.jakartaee-web-api ${jakarta.jakartaee-web-api.version} provided org.junit.jupiter junit-jupiter ${junit.version} test ``` ## Add the Hello Servlet Add the HelloWorldServlet.java file in the `src/main/java/helloworld` directory. ```java package helloworld; import java.io.IOException; import java.io.PrintWriter; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; public class HelloWorldServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try (PrintWriter writer = response.getWriter()) { response.setContentType("text/plain"); writer.println("Hello World!"); writer.flush(); } } } ``` ## Add the web.xml file Add the web.xml file to the `src/main/webapp/WEB-INF` directory. ```xml HelloWorldServlet helloworld.HelloWorldServlet HelloWorldServlet /index.html 30 ``` ## Add an integration test As we want to make sure the application gets tested before we release an integration test is added which will be executed as part of the build. We'll add the integration test to the `src/test/java` directory. ```java package helloworld; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; class HelloWorldIT { @Test void testHelloWorldHtml() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:8080/helloworld/helloworld.html")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello World!")); } } ``` ## Test the application The application is setup to use JUnit to do integration testing using the Piranha Maven plugin so when you are building the application it will also execute an integration test validating the web application works. To build and test the application execute the following command: ```bash mvn install ``` ## Deploy the application To deploy your application you will need 2 pieces. 1. The Piranha Micro runtime JAR. 2. The WAR file you just produced. For the WAR file see the `target` directory. For the Piranha Micro distribution go to Maven Central. And then the following command line will deploy your application: ```bash java -jar piranha-dist-micro.jar --war-file helloworld.war ``` ## Conclusion As you can see getting started with Piranha Micro is straightforward! ================================================ FILE: dist/micro/src/site/markdown/index.md ================================================ # Piranha Micro Piranha Micro is a distribution that bootstrap parts of itself using Maven Central. Beyond that it delivers the same integration as Piranha Web Profile. ## Documentation 1. [Create a Hello World web application](create_a_hello_world_web_application.html) ================================================ FILE: dist/micro/src/site/site.xml ================================================ ================================================ FILE: dist/micro/src/test/java/cloud/piranha/dist/micro/MicroPiranhaIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.dist.micro; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** * The integration test for the MicroPiranha class. * * @author Manfred Riem (mriem@manorrock.com) */ class MicroPiranhaIT { /** * Test the Piranha Micro command line. * * @throws Exception when a serious error occurs. */ @Disabled @Test void testCommandLine() throws Exception { // // TODO - test is disabled because it does not reliable run on Windows. // We need to hardening it so it does. It is apparently not // sufficient to wait 120 seconds. // ProcessBuilder builder = new ProcessBuilder(); builder.command("java", "-jar", "target/piranha-dist-micro.jar", "--http-port", System.getProperty("httpPort")); Process process = builder.start(); Thread.sleep(120000); HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder(new URI("http://localhost:" + System.getProperty("httpPort") + "/")).build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); process.destroyForcibly(); assertEquals(200, response.statusCode()); } } ================================================ FILE: dist/microprofile/pom.xml ================================================ 4.0.0 cloud.piranha.dist project 25.4.0-SNAPSHOT piranha-dist-microprofile jar Piranha - Distribution - MicroProfile cloud.piranha piranha-single ${project.version} compile cloud.piranha.extension piranha-extension-microprofile ${project.version} compile org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test piranha-dist-microprofile org.apache.maven.plugins maven-jar-plugin cloud.piranha.dist.microprofile org.apache.maven.plugins maven-shade-plugin package shade false cloud.piranha.dist.microprofile.MicroProfilePiranhaMain org.apache.maven.plugins maven-surefire-plugin false default file:///tmp/piranha/dist/microprofile/ docker io.fabric8 docker-maven-plugin microprofile ghcr.io/piranhacloud/microprofile:%l linux/amd64 linux/arm64 ${basedir} src/main/docker/Dockerfile build install build push deploy push org.apache.maven.plugins maven-javadoc-plugin true org.sonatype.plugins nexus-staging-maven-plugin true true ================================================ FILE: dist/microprofile/src/main/docker/Dockerfile ================================================ FROM eclipse-temurin:21 RUN useradd -m piranha ADD target/piranha-dist-microprofile.jar /home/piranha WORKDIR /home/piranha RUN chown piranha:piranha piranha-dist-microprofile.jar USER piranha CMD ["java", "-jar", "piranha-dist-microprofile.jar"] ================================================ FILE: dist/microprofile/src/main/java/cloud/piranha/dist/microprofile/MicroProfilePiranhaMain.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.dist.microprofile; import cloud.piranha.extension.microprofile.MicroProfileExtension; import cloud.piranha.single.SingleMain; import cloud.piranha.single.SinglePiranhaBuilder; /** * The Main for Piranha Micro Profile. * *

* This version of Main sets the extension class to the MicroProfileExtension to * deliver Piranha Micro Profile (unless it was overridden). *

* * @author Manfred Riem (mriem@manorrock.com) */ public class MicroProfilePiranhaMain extends SingleMain { /** * Constructor. */ public MicroProfilePiranhaMain() { super(); } /** * Main method. * * @param arguments the arguments. */ public static void main(String[] arguments) { SinglePiranhaBuilder builder = new MicroProfilePiranhaMain().processArguments(arguments); if (builder != null) { if (builder.getConfiguration().getClass("extensionClass") == null) { builder.extensionClass(MicroProfileExtension.class); } builder.build().start(); } else { showHelp(); } } } ================================================ FILE: dist/microprofile/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Piranha Micro Profile distribution. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.dist.microprofile { exports cloud.piranha.dist.microprofile; opens cloud.piranha.dist.microprofile; requires cloud.piranha.extension.microprofile; requires cloud.piranha.single; } ================================================ FILE: dist/platform/pom.xml ================================================ 4.0.0 cloud.piranha.dist project 25.4.0-SNAPSHOT cloud.piranha.dist piranha-dist-platform jar Piranha - Distribution - Platform cloud.piranha.core piranha-core-impl ${project.version} compile cloud.piranha.extension piranha-extension-platform ${project.version} compile cloud.piranha piranha-multi ${project.version} compile piranha-dist-platform org.apache.maven.plugins maven-assembly-plugin package single false src/main/assembly/zip.xml org.apache.maven.plugins maven-failsafe-plugin integration-test verify org.apache.maven.plugins maven-jar-plugin true cloud.piranha.dist.platform.PlatformPiranhaMain org.apache.maven.plugins maven-surefire-plugin ${httpPort} ${httpPort2} ${httpPort2b} ${httpPort2} ${httpPort2b} ${httpPort2} ${httpPort2b} org.codehaus.mojo build-helper-maven-plugin reserve-network-port reserve-network-port validate httpPort httpPort2 httpPort2b httpPort3 httpPort3b httpPort4 httpPort4b default file:///tmp/piranha/dist/platform/ docker io.fabric8 docker-maven-plugin platform ghcr.io/piranhacloud/platform:%l linux/amd64 linux/arm64 ${basedir} src/main/docker/Dockerfile build install build push deploy push org.apache.maven.plugins maven-javadoc-plugin true org.sonatype.plugins nexus-staging-maven-plugin true true ================================================ FILE: dist/platform/src/main/assembly/zip.xml ================================================ zip piranha zip tar.gz ${project.basedir}/src/main/zip ${project.build.directory}/webapps webapps ${project.build.directory}/piranha-dist-platform.jar lib ${project.groupId}:${project.artifactId}:jar:* lib true ================================================ FILE: dist/platform/src/main/docker/Dockerfile ================================================ FROM eclipse-temurin:21 RUN useradd -m piranha ADD target/piranha-dist-platform.tar.gz /home/piranha/ RUN chown -R piranha:piranha /home/piranha WORKDIR /home/piranha/piranha/bin USER piranha CMD ["/bin/bash", "./run.sh"] ================================================ FILE: dist/platform/src/main/java/cloud/piranha/dist/platform/PlatformPiranhaMain.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.dist.platform; import cloud.piranha.extension.platform.PlatformExtension; import cloud.piranha.multi.MultiPiranhaBuilder; import cloud.piranha.multi.MultiPiranhaMain; /** * The Main for Piranha Platform. * * @author Manfred Riem (mriem@manorrock.com) */ public class PlatformPiranhaMain extends MultiPiranhaMain { /** * Constructor. */ public PlatformPiranhaMain() { super(); } /** * Main method. * * @param arguments the arguments. */ public static void main(String[] arguments) { MultiPiranhaBuilder builder = new PlatformPiranhaMain().processArguments(arguments); if (builder != null) { builder.extensionClass(PlatformExtension.class).build().start(); } else { showHelp(); } } } ================================================ FILE: dist/platform/src/main/java/cloud/piranha/dist/platform/PlatformWebApplication.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.dist.platform; import cloud.piranha.core.api.WebApplicationServerRequestMapper; import cloud.piranha.core.impl.DefaultWebApplication; import jakarta.servlet.ServletContext; import java.util.Objects; /** * This web application supports finding other contexts using * {@link ServletContext#getContext(String)}. */ public class PlatformWebApplication extends DefaultWebApplication { /** * Stores the request mapper. */ private final WebApplicationServerRequestMapper requestMapper; /** * Constructor. * * @param requestMapper the request mapper. */ public PlatformWebApplication(WebApplicationServerRequestMapper requestMapper) { this.requestMapper = Objects.requireNonNull(requestMapper); } @Override public ServletContext getContext(String uripath) { return requestMapper.findMapping(uripath); } } ================================================ FILE: dist/platform/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Piranha Platform distribution. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.dist.platform { exports cloud.piranha.dist.platform; opens cloud.piranha.dist.platform; requires transitive cloud.piranha.core.api; requires cloud.piranha.core.impl; requires cloud.piranha.extension.platform; requires cloud.piranha.multi; requires transitive jakarta.servlet; } ================================================ FILE: dist/platform/src/main/zip/etc/logging.properties ================================================ # # Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # # Logging handlers, by default the ConsoleHandler and the FileHandler are # enabled. If you want to add other handlers, see the java.util.logging # documentation for more information. # handlers = java.util.logging.ConsoleHandler java.util.logging.FileHandler # # Global log level # .level = INFO # # The configuration for the ConsoleHandler # # 1. The log level is set to INFO # 2. The formatter used is the SimpleFormatter # # IMPORTANT NOTE # # A Handler (in this case the ConsoleHandler) will only log up up to the level # specified for its Handler. So be default only INFO messages and higher will # show up in the log. E.g if you want FINEST messages to show for a particular # Logger you will have to set the level of the Handler to FINEST as well. # # java.util.logging.ConsoleHandler.level = INFO java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter # # The configuration for the FileHandler # # 1. The log level is set to FINEST # 2. The formatter used is the SimpleFormatter # 3. The size of each log file is 100 MB. # 4. The number of log files to keep is 10 # 5. The handler is set to append to an existing log file. # 6. The filename pattern is tmp/piranha-%g.log java.util.logging.FileHandler.level = FINEST java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter java.util.logging.FileHandler.limit = 10485760 java.util.logging.FileHandler.count = 10 java.util.logging.FileHandler.append = true java.util.logging.FileHandler.pattern = tmp/piranha-%g.log ================================================ FILE: dist/platform/src/main/zip/tmp/README ================================================ README ------ This directory is used for temporary files by Piranha. If the server is NOT running you can safely delete the contents of the directory, but do NOT delete the directory itself. ================================================ FILE: dist/platform/src/main/zip/webapps/README ================================================ README ------ This directory is used for your web applications. If a file ends with the .war extension, or it is a sub directory it will be considered a web application, anything else will be ignored. ================================================ FILE: dist/platform/src/test/java/cloud/piranha/dist/platform/PlatformPiranhaBuilderTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.dist.platform; import cloud.piranha.extension.platform.PlatformExtension; import cloud.piranha.multi.MultiPiranha; import cloud.piranha.multi.MultiPiranhaBuilder; import java.net.ConnectException; import java.net.Socket; import javax.net.SocketFactory; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; /** * The JUnit tests for the PlatformPiranhaBuilder class. * * @author Manfred Riem (mriem@manorrock.com) */ class PlatformPiranhaBuilderTest { /** * Test httpPort method. * * @throws Exception when a serious error occurs. */ @Test void testHttpPort() throws Exception { MultiPiranha piranha = new MultiPiranhaBuilder() .extensionClass(PlatformExtension.class) .httpPort(Integer.parseInt(System.getProperty("httpPort"))) .build(); piranha.start(); Thread.sleep(5000); try (Socket socket = new Socket("localhost", Integer.parseInt(System.getProperty("httpPort")))) { assertNotNull(socket.getOutputStream()); } piranha.stop(); Thread.sleep(5000); } /** * Test httpsPort method. * * @throws Exception when a serious error occurs. */ @Test void testHttpPort2() throws Exception { MultiPiranha piranha = new MultiPiranhaBuilder() .extensionClass(PlatformExtension.class) .httpPort(-1) .httpsPort(Integer.parseInt(System.getProperty("httpPort2b"))) .build(); piranha.start(); Thread.sleep(5000); try ( Socket socket = new Socket("localhost", Integer.parseInt(System.getProperty("httpPort2")))) { fail(); } catch (ConnectException e) { } piranha.stop(); Thread.sleep(5000); } /** * Test httpsPort method. * * @throws Exception when a serious error occurs. */ @Test void testHttpsPort2() throws Exception { MultiPiranha piranha = new MultiPiranhaBuilder() .extensionClass(PlatformExtension.class) .httpsKeystoreFile("src/main/zip/etc/keystore.jks") .httpsKeystorePassword("password") .httpPort(Integer.parseInt(System.getProperty("httpPort3"))) .httpsPort(Integer.parseInt(System.getProperty("httpPort3b"))) .build(); piranha.start(); Thread.sleep(5000); SocketFactory factory = SSLSocketFactory.getDefault(); try (SSLSocket socket = (SSLSocket) factory.createSocket("localhost", Integer.parseInt(System.getProperty("httpPort3b")))) { assertNotNull(socket.getOutputStream()); assertNotNull(socket.getSSLParameters()); assertEquals("TLSv1.3", socket.getSSLParameters().getProtocols()[0]); } piranha.stop(); Thread.sleep(5000); } /** * Test extensionClass method. * * @throws Exception when a serious error occurs. */ @Test void testDefaultExtensionClass() throws Exception { MultiPiranha piranha = new MultiPiranhaBuilder() .extensionClass(PlatformExtension.class.getName()) .httpPort(Integer.parseInt(System.getProperty("httpPort4"))) .verbose(true) .build(); piranha.start(); Thread.sleep(5000); try (Socket socket = new Socket("localhost", Integer.parseInt(System.getProperty("httpPort4")))) { assertNotNull(socket.getOutputStream()); } catch (ConnectException e) { } piranha.stop(); Thread.sleep(5000); } } ================================================ FILE: dist/platform/src/test/java/cloud/piranha/dist/platform/PlatformPiranhaIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.dist.platform; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit integration tests for ServerPiranha class. * * @author Manfred Riem (mriem@manorrock.com) */ class PlatformPiranhaIT { /** * Extract the zip input stream. * * @param zipInput the zip input stream. * @param filePath the file path. * @throws IOException when an I/O error occurs. */ private void extractZipInputStream(ZipInputStream zipInput, String filePath) throws IOException { try (BufferedOutputStream bufferOutput = new BufferedOutputStream(new FileOutputStream(filePath))) { byte[] bytesIn = new byte[8192]; int read; while ((read = zipInput.read(bytesIn)) != -1) { bufferOutput.write(bytesIn, 0, read); } } } /** * Extract the Server zip file. */ private void extractServer(File zipFile) { try (ZipInputStream zipInput = new ZipInputStream(new FileInputStream(zipFile))) { ZipEntry entry = zipInput.getNextEntry(); while (entry != null) { String filePath = "target" + File.separatorChar + entry.getName(); if (!entry.isDirectory()) { File file = new File(filePath); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } extractZipInputStream(zipInput, filePath); } zipInput.closeEntry(); entry = zipInput.getNextEntry(); } } catch (IOException ioe) { } } /** * Test run method. * * @throws Exception when a serious error occurs. */ @Test void testRun() throws Exception { extractServer(new File("target/piranha-dist-platform.zip")); ProcessBuilder builder = new ProcessBuilder(); Process process; if (System.getProperty("os.name").toLowerCase().contains("windows")) { process = builder. directory(new File("target/piranha/bin")). command("cmd", "/c", "start.cmd"). start(); } else { process = builder. directory(new File("target/piranha/bin")). command("sh", "./start.sh"). start(); } process.waitFor(5, TimeUnit.SECONDS); Thread.sleep(5000); File pidFile = new File("target/piranha/tmp/piranha.pid"); assertTrue(pidFile.exists()); if (System.getProperty("os.name").toLowerCase().contains("windows")) { process = builder. directory(new File("target/piranha/bin")). command("cmd", "/c", "stop.cmd"). start(); } else { process = builder. directory(new File("target/piranha/bin")). command("sh", "./stop.sh"). start(); } process.waitFor(5, TimeUnit.SECONDS); Thread.sleep(5000); assertFalse(pidFile.exists()); } } ================================================ FILE: dist/pom.xml ================================================ 4.0.0 cloud.piranha project 25.4.0-SNAPSHOT cloud.piranha.dist project pom Piranha - Distribution coreprofile isolated micro microprofile platform server servlet webprofile org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test default file:///tmp/piranha/dist/ ================================================ FILE: dist/server/pom.xml ================================================ 4.0.0 cloud.piranha.dist project 25.4.0-SNAPSHOT cloud.piranha.dist piranha-dist-server jar Piranha - Distribution - Server cloud.piranha.core piranha-core-impl ${project.version} compile cloud.piranha.extension piranha-extension-servlet ${project.version} compile cloud.piranha piranha-multi ${project.version} compile piranha-dist-server org.apache.maven.plugins maven-assembly-plugin package single false src/main/assembly/zip.xml org.apache.maven.plugins maven-failsafe-plugin integration-test verify org.apache.maven.plugins maven-jar-plugin true cloud.piranha.dist.server.ServerPiranhaMain org.apache.maven.plugins maven-surefire-plugin ${httpPort} ${httpPort2} ${httpsPort2} org.codehaus.mojo build-helper-maven-plugin reserve-network-port reserve-network-port validate httpPort httpPort2 httpsPort2 default file:///tmp/piranha/dist/server/ docker io.fabric8 docker-maven-plugin server ghcr.io/piranhacloud/server:%l linux/amd64 linux/arm64 ${basedir} src/main/docker/Dockerfile build install build push deploy push org.apache.maven.plugins maven-javadoc-plugin true org.sonatype.plugins nexus-staging-maven-plugin true true grizzly cloud.piranha.http piranha-http-grizzly ${project.version} runtime impl true jdk cloud.piranha.http piranha-http-jdk ${project.version} runtime netty cloud.piranha.http piranha-http-netty ${project.version} runtime undertow cloud.piranha.http piranha-http-undertow ${project.version} runtime ================================================ FILE: dist/server/src/main/assembly/zip.xml ================================================ zip piranha zip tar.gz ${project.basedir}/src/main/zip ${project.build.directory}/webapps webapps ${project.build.directory}/piranha-dist-server.jar lib ${project.groupId}:${project.artifactId}:jar:* lib true ================================================ FILE: dist/server/src/main/docker/Dockerfile ================================================ FROM eclipse-temurin:21 RUN useradd -m piranha ADD target/piranha-dist-server.tar.gz /home/piranha/ RUN chown -R piranha:piranha /home/piranha WORKDIR /home/piranha/piranha/bin USER piranha CMD ["/bin/bash", "./run.sh"] ================================================ FILE: dist/server/src/main/java/cloud/piranha/dist/server/ServerPiranhaMain.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.dist.server; import cloud.piranha.extension.servlet.ServletExtension; import cloud.piranha.multi.MultiPiranhaBuilder; import cloud.piranha.multi.MultiPiranhaMain; /** * The Main for Piranha Server. * * @author Manfred Riem (mriem@manorrock.com) */ public class ServerPiranhaMain extends MultiPiranhaMain { /** * Constructor. */ public ServerPiranhaMain() { super(); } /** * Main method. * * @param arguments the arguments. */ public static void main(String[] arguments) { MultiPiranhaBuilder builder = new ServerPiranhaMain().processArguments(arguments); if (builder != null) { builder.extensionClass(ServletExtension.class).build().start(); } else { showHelp(); } } } ================================================ FILE: dist/server/src/main/java/cloud/piranha/dist/server/ServerWebApplication.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.dist.server; import cloud.piranha.core.api.WebApplicationServerRequestMapper; import cloud.piranha.core.impl.DefaultWebApplication; import jakarta.servlet.ServletContext; import java.util.Objects; /** * This web application supports finding other contexts using * {@link ServletContext#getContext(String)}. */ public class ServerWebApplication extends DefaultWebApplication { /** * Stores the request mapper. */ private final WebApplicationServerRequestMapper requestMapper; /** * Constructor. * * @param requestMapper the request mapper. */ public ServerWebApplication(WebApplicationServerRequestMapper requestMapper) { this.requestMapper = Objects.requireNonNull(requestMapper); } @Override public ServletContext getContext(String uripath) { return requestMapper.findMapping(uripath); } } ================================================ FILE: dist/server/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Piranha Server distribution. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.dist.server { exports cloud.piranha.dist.server; opens cloud.piranha.dist.server; requires transitive cloud.piranha.core.api; requires cloud.piranha.core.impl; requires cloud.piranha.extension.servlet; requires cloud.piranha.multi; } ================================================ FILE: dist/server/src/main/zip/etc/logging.properties ================================================ # # Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # # Logging handlers, by default the ConsoleHandler and the FileHandler are # enabled. If you want to add other handlers, see the java.util.logging # documentation for more information. # handlers = java.util.logging.ConsoleHandler java.util.logging.FileHandler # # Global log level # .level = INFO # # The configuration for the ConsoleHandler # # 1. The log level is set to INFO # 2. The formatter used is the SimpleFormatter # # IMPORTANT NOTE # # A Handler (in this case the ConsoleHandler) will only log up up to the level # specified for its Handler. So be default only INFO messages and higher will # show up in the log. E.g if you want FINEST messages to show for a particular # Logger you will have to set the level of the Handler to FINEST as well. # # java.util.logging.ConsoleHandler.level = INFO java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter # # The configuration for the FileHandler # # 1. The log level is set to FINEST # 2. The formatter used is the SimpleFormatter # 3. The size of each log file is 100 MB. # 4. The number of log files to keep is 10 # 5. The handler is set to append to an existing log file. # 6. The filename pattern is tmp/piranha-%g.log java.util.logging.FileHandler.level = FINEST java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter java.util.logging.FileHandler.limit = 10485760 java.util.logging.FileHandler.count = 10 java.util.logging.FileHandler.append = true java.util.logging.FileHandler.pattern = tmp/piranha-%g.log ================================================ FILE: dist/server/src/main/zip/tmp/README ================================================ README ------ This directory is used for temporary files by Piranha. If the server is NOT running you can safely delete the contents of the directory, but do NOT delete the directory itself. ================================================ FILE: dist/server/src/main/zip/webapps/README ================================================ README ------ This directory is used for your web applications. If a file ends with the .war extension, or it is a sub directory it will be considered a web application, anything else will be ignored. ================================================ FILE: dist/server/src/site/markdown/create_a_hello_world_web_application.md ================================================ # Create a Hello World web application If you are looking to create a simple Hello World web application with Piranha Server to get started then look no further! In 5 steps you will learn how to create the web application. They are: 1. Create the Maven POM file 1. Add the helloworld.html page 1. Add an integration test 1. Test the application 1. Deploy the application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guides.server helloworld 24.9.0 war Create a Hello World application 5.11.0 21 server ${project.version} UTF-8 3.13.0 3.5.0 3.4.0 org.junit.jupiter junit-jupiter-api ${junit.version} test org.junit.jupiter junit-jupiter-engine ${junit.version} test org.junit.jupiter junit-jupiter-params ${junit.version} test helloworld cloud.piranha.maven piranha-maven-plugin ${piranha.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop ${piranha.distribution} org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false ``` ## Add the Hello World HTML file Add the helloworld.html file in the `src/main/webapp` directory. ```html Hello World
Hello World!
``` ## Add an integration test As we want to make sure the application gets tested before we release an integration test is added which will be executed as part of the build. We'll add the integration test to the `src/test/java` directory. ```java package helloworld; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; class HelloWorldIT { @Test void testHelloWorldHtml() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:8080/helloworld/helloworld.html")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello World!")); } } ``` ## Test the application The application is setup to use JUnit to do integration testing using the Piranha Maven plugin so when you are building the application it will also execute an integration test validating the web application works. To build and test the application execute the following command: ```bash mvn install ``` ## Deploy the application To deploy your application you will need 2 pieces. 1. The Piranha Server distribution zip. 2. The WAR file you just produced. For the Piranha Server distribution zip go to Maven Central. For the WAR file see the `target` directory. Once you have downloaded the Piranha Server distribution zip extract it into an empty directory and then copy the WAR file in the `webapps` directory. From the `bin` directory of Piranha Server start the server using the command line below: ```bash ./start.sh ``` ## Conclusion As you can see getting started with Piranha Server is straightforward! ================================================ FILE: dist/server/src/site/markdown/index.md ================================================ # Piranha Server Piranha Server is our distribution that delivers you the equivalent of what Tomcat and Jetty deliver to you. If you are looking to make an easy jump from Tomcat or Jetty then try out this distribution! The following components are available in the Piranha Server distribution: * Jakarta Authentication * Jakarta Expression Language * Jakarta Pages * Jakarta Servlet * Jakarta WebSocket ## Download Piranha Server Piranha Server is available as a zip or tar.gz file from Maven Central. The download link below will take you to the location on Maven Central where you can download any version of Piranha Server. Select the version by clicking on the directory link and then download the zip or tar.gz file from there. Download Note the name of the JAR file you want to download should look like piranha-dist-server-X.Y.Z.zip or piranha-dist-server-X.Y.Z.tar.gz ## Starting the server Assuming you have downloaded the zip or tar.gz file as previously indicated you will need to unzip it into a directory of your choice. Then by going to the `bin` directory you can run Piranha Server by issuing the following command line: ``` ./start.sh ``` On Windows use `start.cmd` instead. ## Stopping the server To stop the server use: ``` ./stop.sh ``` On Windows use `stop.cmd` instead. ## Deploying your application Deploying your web application is as simple as copying your WAR file into the `webapps` directory of the server and then stopping and starting the server. ## Running using Docker If you want to run Piranha Server using Docker we have a base image available for you using Eclipse Temurin as the Java runtime. See https://github.com/orgs/piranhacloud/packages/container/package/server for more information and how to pull that image. To use this as a base image you would create your own Dockerfile and add your WAR file using a COPY as illustrated below: ```Dockerfile FROM ghcr.io/piranhacloud/server:latest COPY target/my.war /home/piranha/piranha/webapps/my.war ``` ## Documentation 1. [Create a Hello World web application](create_a_hello_world_web_application.html) ================================================ FILE: dist/server/src/site/site.xml ================================================ ================================================ FILE: dist/server/src/test/java/cloud/piranha/dist/server/ServerPiranhaBuilderTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.dist.server; import cloud.piranha.extension.servlet.ServletExtension; import cloud.piranha.multi.MultiPiranha; import cloud.piranha.multi.MultiPiranhaBuilder; import java.net.ConnectException; import java.net.Socket; import javax.net.SocketFactory; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; /** * The JUnit tests for the ServerPiranhaBuilder class. * * @author Manfred Riem (mriem@manorrock.com) */ class ServerPiranhaBuilderTest { /** * Test httpPort method. * * @throws Exception when a serious error occurs. */ @Test void testHttpPort() throws Exception { MultiPiranha piranha = new MultiPiranhaBuilder() .extensionClass(ServletExtension.class) .httpPort(Integer.parseInt(System.getProperty("httpPort"))) .build(); piranha.start(); Thread.sleep(5000); try ( Socket socket = new Socket("localhost", Integer.parseInt(System.getProperty("httpPort")))) { assertNotNull(socket.getOutputStream()); } piranha.stop(); Thread.sleep(5000); } /** * Test httpsPort method. * * @throws Exception when a serious error occurs. */ @Test void testHttpPort2() throws Exception { MultiPiranha piranha = new MultiPiranhaBuilder() .extensionClass(ServletExtension.class) .httpPort(-1) .httpsPort(Integer.parseInt(System.getProperty("httpsPort2"))) .build(); piranha.start(); Thread.sleep(5000); try ( Socket socket = new Socket("localhost", Integer.parseInt(System.getProperty("httpPort2")))) { fail(); } catch (ConnectException e) { } piranha.stop(); Thread.sleep(5000); } /** * Test httpsPort method. * * @throws Exception when a serious error occurs. */ @Test void testHttpsPort2() throws Exception { MultiPiranha piranha = new MultiPiranhaBuilder() .extensionClass(ServletExtension.class) .httpsKeystoreFile("src/main/zip/etc/keystore.jks") .httpsKeystorePassword("password") .httpPort(8228) .httpsPort(8338) .build(); piranha.start(); Thread.sleep(5000); SocketFactory factory = SSLSocketFactory.getDefault(); try ( SSLSocket socket = (SSLSocket) factory.createSocket("localhost", 8338)) { assertNotNull(socket.getOutputStream()); assertNotNull(socket.getSSLParameters()); assertEquals("TLSv1.3", socket.getSSLParameters().getProtocols()[0]); } piranha.stop(); Thread.sleep(5000); } /** * Test extensionClass method. * * @throws Exception when a serious error occurs. */ @Test void testDefaultExtensionClass() throws Exception { MultiPiranha piranha = new MultiPiranhaBuilder() .extensionClass(ServletExtension.class.getName()) .httpPort(8080) .verbose(true) .build(); piranha.start(); Thread.sleep(5000); try ( Socket socket = new Socket("localhost", 8080)) { assertNotNull(socket.getOutputStream()); } catch (ConnectException e) { } piranha.stop(); Thread.sleep(5000); } } ================================================ FILE: dist/server/src/test/java/cloud/piranha/dist/server/ServerPiranhaIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.dist.server; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit integration tests for ServerPiranha class. * * @author Manfred Riem (mriem@manorrock.com) */ class ServerPiranhaIT { /** * Extract the zip input stream. * * @param zipInput the zip input stream. * @param filePath the file path. * @throws IOException when an I/O error occurs. */ private void extractZipInputStream(ZipInputStream zipInput, String filePath) throws IOException { try (BufferedOutputStream bufferOutput = new BufferedOutputStream(new FileOutputStream(filePath))) { byte[] bytesIn = new byte[8192]; int read; while ((read = zipInput.read(bytesIn)) != -1) { bufferOutput.write(bytesIn, 0, read); } } } /** * Extract the Server zip file. */ private void extractServer(File zipFile) { try (ZipInputStream zipInput = new ZipInputStream(new FileInputStream(zipFile))) { ZipEntry entry = zipInput.getNextEntry(); while (entry != null) { String filePath = "target" + File.separatorChar + entry.getName(); if (!entry.isDirectory()) { File file = new File(filePath); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } extractZipInputStream(zipInput, filePath); } zipInput.closeEntry(); entry = zipInput.getNextEntry(); } } catch (IOException ioe) { } } /** * Test run method. * * @throws Exception when a serious error occurs. */ @Test void testRun() throws Exception { extractServer(new File("target/piranha-dist-server.zip")); ProcessBuilder builder = new ProcessBuilder(); Process process; if (System.getProperty("os.name").toLowerCase().contains("windows")) { process = builder. directory(new File("target/piranha/bin")). command("cmd", "/c", "start.cmd"). start(); } else { process = builder. directory(new File("target/piranha/bin")). command("sh", "./start.sh"). start(); } process.waitFor(5, TimeUnit.SECONDS); Thread.sleep(5000); File pidFile = new File("target/piranha/tmp/piranha.pid"); assertTrue(pidFile.exists()); if (System.getProperty("os.name").toLowerCase().contains("windows")) { process = builder. directory(new File("target/piranha/bin")). command("cmd", "/c", "stop.cmd"). start(); } else { process = builder. directory(new File("target/piranha/bin")). command("sh", "./stop.sh"). start(); } process.waitFor(5, TimeUnit.SECONDS); Thread.sleep(5000); assertFalse(pidFile.exists()); } } ================================================ FILE: dist/servlet/pom.xml ================================================ 4.0.0 cloud.piranha.dist project 25.4.0-SNAPSHOT cloud.piranha.dist piranha-dist-servlet jar Piranha - Distribution - Servlet cloud.piranha piranha-single ${project.version} compile cloud.piranha.extension piranha-extension-servlet ${project.version} compile piranha-dist-servlet org.apache.maven.plugins maven-jar-plugin cloud.piranha.dist.servlet org.apache.maven.plugins maven-shade-plugin package shade false cloud.piranha.dist.servlet.ServletPiranhaMain default file:///tmp/piranha/dist/servlet/ docker io.fabric8 docker-maven-plugin servlet ghcr.io/piranhacloud/servlet:%l linux/amd64 linux/arm64 ${basedir} src/main/docker/Dockerfile build install build push deploy push org.apache.maven.plugins maven-javadoc-plugin true org.sonatype.plugins nexus-staging-maven-plugin true true ================================================ FILE: dist/servlet/src/main/docker/Dockerfile ================================================ FROM eclipse-temurin:21 RUN useradd -m piranha ADD target/piranha-dist-servlet.jar /home/piranha WORKDIR /home/piranha RUN chown piranha:piranha piranha-dist-servlet.jar USER piranha CMD ["java", "-jar", "piranha-dist-servlet.jar"] ================================================ FILE: dist/servlet/src/main/java/cloud/piranha/dist/servlet/ServletPiranhaMain.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.dist.servlet; import cloud.piranha.extension.servlet.ServletExtension; import cloud.piranha.single.SingleMain; import cloud.piranha.single.SinglePiranhaBuilder; /** * The Main for Piranha Servlet. * *

* This version of Main sets the extension class to the ServletExtension to * deliver Piranha Servlet (unless it was overridden). *

* * @author Manfred Riem (mriem@manorrock.com) */ public class ServletPiranhaMain extends SingleMain { /** * Main method. * * @param arguments the arguments. */ public static void main(String[] arguments) { SinglePiranhaBuilder builder = new ServletPiranhaMain().processArguments(arguments); if (builder != null) { if (builder.getConfiguration().getClass("extensionClass") == null) { builder.extensionClass(ServletExtension.class); } builder.build().start(); } else { showHelp(); } } } ================================================ FILE: dist/servlet/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Piranha Servlet distribution. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.dist.servlet { exports cloud.piranha.dist.servlet; opens cloud.piranha.dist.servlet; requires cloud.piranha.extension.servlet; requires cloud.piranha.single; } ================================================ FILE: dist/servlet/src/site/markdown/create_a_hello_world_web_application.md ================================================ # Create a Hello World web application If you are looking to create a simple Hello World web application with Piranha Servlet to get started then look no further! In 5 steps you will learn how to create the web application. They are: 1. Create the Maven POM file 1. Add the helloworld.html page 1. Add an integration test 1. Test the application 1. Deploy the application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guides.servlet helloworld 24.11.0-SNAPSHOT war Piranha Servlet - HelloWorld web application 17 5.10.0-M1 3.11.0 3.0.0 3.3.2 servlet 23.6.0 UTF-8 org.junit.jupiter junit-jupiter-api ${junit.version} test org.junit.jupiter junit-jupiter-engine ${junit.version} test org.junit.jupiter junit-jupiter-params ${junit.version} test helloworld cloud.piranha.maven.plugins piranha-maven-plugin ${piranha.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop ${piranha.distribution} org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false ``` ## Add the Hello World HTML file Add the helloworld.html file in the `src/main/webapp` directory. ```html Hello World
Hello World!
``` ## Add an integration test As we want to make sure the application gets tested before we release an integration test is added which will be executed as part of the build. We'll add the integration test to the `src/test/java` directory. ```java package helloworld; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; class HelloWorldIT { @Test void testHelloWorldHtml() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:8080/helloworld/helloworld.html")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello World!")); } } ``` ## Test the application The application is setup to use JUnit to do integration testing using the Piranha Maven plugin so when you are building the application it will also execute an integration test validating the web application works. To build and test the application execute the following command: ```bash mvn install ``` ## Deploy the application To deploy your application you will need 2 pieces. 1. The Piranha Servlet runtime JAR. 2. The WAR file you just produced. For the WAR file see the `target` directory. For the Piranha Servlet distribution go to Maven Central. And then the following command line will deploy your application: ```bash java -jar piranha-dist-servlet.jar --war-file helloworld.war ``` ## Conclusion As you can see getting started with Piranha Servlet is easy! ================================================ FILE: dist/servlet/src/site/markdown/create_a_jakarta_pages_application.md ================================================ # Create a Jakarta Pages application If you are looking to use Piranha Servlet with Jakarta Page you have come to the right place! In 5 steps you will learn how to use Jakarta Pages on Piranha Servlet. They are: 1. Create the Maven POM file 1. Add the hellopages.jsp page 1. Add an integration test 1. Test the application 1. Deploy the application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guides.servlet pages 24.11.0-SNAPSHOT war Create a Jakarta Pages application on Piranha Servlet 5.10.2 24.4.0 21 servlet UTF-8 3.13.0 3.2.5 3.4.0 org.junit.jupiter junit-jupiter-api ${junit.version} test org.junit.jupiter junit-jupiter-engine ${junit.version} test org.junit.jupiter junit-jupiter-params ${junit.version} test pages cloud.piranha.maven.plugins piranha-maven-plugin ${piranha.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop servlet org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false ``` ## Add the Hello Pages JSP file Add the hellopages.jsp file in the `src/main/webapp` directory. ```html <%@page contentType="text/html" pageEncoding="UTF-8"%>

Hello from Jakarta Pages!

``` ## Add an integration test As we want to make sure the application gets tested before we release an integration test is added which will be executed as part of the build. We'll add the integration test to the `src/test/java` directory. ```java package hello; import java.net.URI; import java.net.http.HttpClient; import static java.net.http.HttpClient.Redirect.ALWAYS; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import java.time.Duration; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; public class HelloIT { @Test public void testHelloPagesJsp() throws Exception { HttpClient client = HttpClient .newBuilder() .connectTimeout(Duration.ofSeconds(60)) .followRedirects(ALWAYS) .build(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:8080/pages/hellopages.jsp")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello from Jakarta Pages!")); } } ``` ## Test the application The application is setup to use JUnit to do integration testing using the Piranha Maven plugin so when you are building the application it will also execute an integration test validating the web application works. To build and test the application execute the following command: ```bash mvn install ``` ## Deploy the application To deploy your application you will need 2 pieces. 1. The Piranha Servlet runtime JAR. 2. The WAR file you just produced. For the WAR file see the `target` directory. For the Piranha Servlet distribution go to Maven Central. And then the following command line will deploy your application: ```bash java -jar piranha-dist-servlet.jar --war-file pages.war ``` ## Conclusion As you can see using Jakarta Pages on Piranha Servlet is very easy! ================================================ FILE: dist/servlet/src/site/markdown/create_a_websocket_application.md ================================================ # Create a WebSocket application If you are looking to use Piranha Servlet with WebSockets then you landed at the right spot! In 4 steps you will learn how to use WebSockets on Piranha Servlet. They are: 1. Create the Maven POM file 1. Add the ChatEndpoint.java file 1. Deploy the application 1. Test the application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guides.servlet websocket 24.11.0-SNAPSHOT war Piranha Servlet - WebSocket Chat application 2.1.0 17 3.11.0 3.3.2 servlet 23.6.0 UTF-8 jakarta.websocket jakarta.websocket-api ${jakarta.websocket.version} provided jakarta.websocket jakarta.websocket-client-api ${jakarta.websocket.version} provided websocket cloud.piranha.maven.plugins piranha-maven-plugin ${piranha.version} org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false ``` ## Add the ChatEndpoint.java file Add the ChatEndpoint.java file in the `src/main/java/chat` directory. ```java package chat; import jakarta.websocket.OnMessage; import jakarta.websocket.Session; import jakarta.websocket.server.ServerEndpoint; @ServerEndpoint("/chat") public class ChatEndpoint { @OnMessage public String onMessage(String message, Session session) { return message; } } ``` ## Deploy the application To deploy your application you will need 2 pieces. 1. The Piranha Servlet runtime JAR. 2. The WAR file you just produced. For the WAR file see the `target` directory. For the Piranha Servlet distribution go to Maven Central. And then the following command line will deploy your application: ```bash java -jar piranha-dist-servlet.jar --war-file websocket.war ``` ## Test the application To test the application connect to `ws://localhost:8080/websocket/chat` using a WebSocket client. ## Conclusion As you can creating a WebSocket application using Piranha Servlet is quite easy! ================================================ FILE: dist/servlet/src/site/markdown/index.md ================================================ # Piranha Servlet Piranha Servlet is our distribution that delivers you with the equivalent of what Tomcat and Jetty deliver to you packaged up as a single runnable JAR. If you are looking to run a single web application AND you want make an jump from Tomcat or Jetty to Piranha, then try out this distribution! The following components are available in the Piranha Servlet distribution: * Jakarta Authentication * Jakarta Expression Language * Jakarta Pages * Jakarta Servlet * Jakarta WebSocket ## Download Piranha Servlet Piranha Servlet is available as a JAR file from Maven Central. The download link below will take you to the location on Maven Central where you can download any version of Piranha Servlet Select the version by clicking on the directory link and then download the JAR file from there. Download Note the name of the JAR file you want to download should look like piranha-dist-servlet-X.Y.Z.jar ## Quickstart Assuming you have downloaded the JAR file as previously indicated running Piranha Servlet is as simple as: ``` java -jar piranha-dist-servlet-X.Y.Z.jar --war-file your-webapplication.war ``` ## Running using Docker If you want to run Piranha Servlet using Docker we have a base image available for you using Eclipse Temurin as the underlying runtime. See https://github.com/piranhacloud/piranha/pkgs/container/servlet for more information and how to pull that image. To use this as a base image you would create your own Dockerfile and add your WAR file using a COPY and change the CMD to point to your WAR file as illustrated below: ```Dockerfile FROM ghcr.io/piranhacloud/servlet:latest COPY target/my.war my.war USER root RUN chown -R piranha:piranha /home/piranha USER piranha CMD ["java", "-jar", "piranha-dist-servlet.jar", "--war-file", "my.war"] ``` ## Guides 1. [Create a Hello World web application](create_a_hello_world_web_application.html) 1. [Create a Jakarta Pages application](create_a_jakarta_pages_application.html) 1. [Create a WebSocket application](create_a_websocket_application.html) 1. [Run a web application on CRaC](run_a_web_application_on_crac.html) ================================================ FILE: dist/servlet/src/site/markdown/run_a_web_application_on_crac.md ================================================ # Run a web application on CRaC If you are looking to get CRaC-ing with Piranha Servlet you can follow along! _Make sure you are using a CRaC enabled JDK available from https://github.com/CRaC/openjdk-builds/releases_ In 10 steps you will learn how to deploy CRaC with Piranha Servlet. They are: 1. Create the Maven POM file 1. Add the hello.xhtml page 1. Add the HelloBean.java file 1. Add the web.xml file 1. Add the beans.xml file 1. Add an integration test 1. Test the application 1. Start the application 1. Create the checkpoint 1. Restart the application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guides.servlet crac 1-SNAPSHOT war Run a web application on Piranha Servlet with CRaC 5.11.0-M1 4.1.1 24.11.0 6.0.0.Beta4 21 servlet UTF-8 3.13.0 3.2.5 3.4.0 org.glassfish jakarta.faces ${mojarra.version} compile org.jboss.weld.servlet weld-servlet-shaded ${weld.version} compile cloud.piranha.extension piranha-extension-weld ${piranha.version} runtime org.junit.jupiter junit-jupiter-api ${junit.version} test org.junit.jupiter junit-jupiter-engine ${junit.version} test org.junit.jupiter junit-jupiter-params ${junit.version} test crac cloud.piranha.maven.plugins piranha-maven-plugin ${piranha.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop servlet org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false ``` ## Add the Hello XHTML file Add the helloworld.xhtml file in the `src/main/webapp` directory. ```html Jakarta Faces application
Jakarta Faces application

``` ## Add the HelloBean.java file Add the HelloBean.java file in the `src/main/java/hello` directory. ```java package hello; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Named; @Named(value = "helloBean") @RequestScoped public class HelloBean { private String hello = "Hello from CRaC!"; public String getHello() { return hello; } public void setHello(String hello) { this.hello = hello; } } ``` ## Add the web.xml file Add the web.xml file in the `src/main/webapp/WEB-INF` directory. ```xml Faces Servlet jakarta.faces.webapp.FacesServlet 1 Faces Servlet *.xhtml ``` ## Add the beans.xml file Add the beans.xml file in the `src/main/webapp/WEB-INF` directory. ```xml ``` ## Add an integration test As we want to make sure the application gets tested before we release an integration test is added which will be executed as part of the build. We'll add the integration test to the `src/test/java` directory. ```java package hello; import java.net.URI; import java.net.http.HttpClient; import static java.net.http.HttpClient.Redirect.ALWAYS; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import java.time.Duration; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; public class HelloIT { @Test public void testHelloXhtml() throws Exception { HttpClient client = HttpClient .newBuilder() .connectTimeout(Duration.ofSeconds(60)) .followRedirects(ALWAYS) .build(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:8080/crac/hello.xhtml")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello from CRaC!")); } } ``` ## Test the application The application is setup to use JUnit to do integration testing using the Piranha Maven plugin so when you are building the application it will also execute an integration test validating the web application works. To build and test the application execute the following command: ```bash mvn install ``` ## Deploy the application To deploy your application you will need 2 pieces. 1. The Piranha Servlet runtime JAR. 2. The WAR file you just produced. For the WAR file see the `target` directory. For the Piranha Servlet distribution go to Maven Central. And then the following command line will deploy your application: ```bash java -XX:CRaCCheckpointTo=cr -jar piranha-dist-servlet.jar --enable-crac --write-pid --war-file crac.war & ``` ## Create the checkpoint You have your application currently running so now it is time to create a checkpoint. To ask the JVM to create a checkpoint use the command line below: ```bash jcmd piranha-dist-servlet.jar JDK.checkpoint ``` The application will exit and the chechkpoint files are in the `cr` directory. ## Restart the application To restart the application for the checkpoint you can use the following command line: ```bash java -XX:CRaCRestoreFrom=cr ``` ## Conclusion As you can see using CRaC with Piranha Servlet is matter of the right JDK and the right command line switch. ## References 1. [CRaC Project](https://wiki.openjdk.org/display/crac/Main) ================================================ FILE: dist/servlet/src/site/site.xml ================================================ ================================================ FILE: dist/webprofile/pom.xml ================================================ 4.0.0 cloud.piranha.dist project 25.4.0-SNAPSHOT piranha-dist-webprofile jar Piranha - Distribution - WebProfile cloud.piranha piranha-single ${project.version} compile cloud.piranha.extension piranha-extension-webprofile ${project.version} compile org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test piranha-dist-webprofile org.apache.maven.plugins maven-jar-plugin cloud.piranha.dist.webprofile org.apache.maven.plugins maven-shade-plugin package shade false cloud.piranha.dist.webprofile.WebProfilePiranhaMain org.apache.maven.plugins maven-surefire-plugin false default file:///tmp/piranha/dist/webprofile/ docker io.fabric8 docker-maven-plugin webprofile ghcr.io/piranhacloud/webprofile:%l linux/amd64 linux/arm64 ${basedir} src/main/docker/Dockerfile build install build push deploy push org.apache.maven.plugins maven-javadoc-plugin true org.sonatype.plugins nexus-staging-maven-plugin true true ================================================ FILE: dist/webprofile/src/main/docker/Dockerfile ================================================ FROM eclipse-temurin:21 RUN useradd -m piranha ADD target/piranha-dist-webprofile.jar /home/piranha WORKDIR /home/piranha RUN chown piranha:piranha piranha-dist-webprofile.jar USER piranha CMD ["java", "-jar", "piranha-dist-webprofile.jar"] ================================================ FILE: dist/webprofile/src/main/java/cloud/piranha/dist/webprofile/WebProfilePiranhaMain.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.dist.webprofile; import cloud.piranha.extension.webprofile.WebProfileExtension; import cloud.piranha.single.SingleMain; import cloud.piranha.single.SinglePiranhaBuilder; /** * The Main for Piranha Web Profile. * *

* This version of Main sets the extension class to the WebProfileExtension to * deliver Piranha Core Profile (unless it was overridden). *

* * @author Manfred Riem (mriem@manorrock.com) */ public class WebProfilePiranhaMain extends SingleMain { /** * Constructor. */ public WebProfilePiranhaMain() { super(); } /** * Main method. * * @param arguments the arguments. */ public static void main(String[] arguments) { SinglePiranhaBuilder builder = new WebProfilePiranhaMain().processArguments(arguments); if (builder != null) { if (builder.getConfiguration().getClass("extensionClass") == null) { builder.extensionClass(WebProfileExtension.class); } builder.build().start(); } else { showHelp(); } } } ================================================ FILE: dist/webprofile/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Piranha Web Profile distribution. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.dist.webprofile { exports cloud.piranha.dist.webprofile; opens cloud.piranha.dist.webprofile; requires cloud.piranha.extension.webprofile; requires cloud.piranha.single; } ================================================ FILE: dist/webprofile/src/site/markdown/create_a_faces_application.md ================================================ # Create a Faces application If you are looking to use Piranha Web Profile with Jakarta Faces then read this! In 8 steps you will learn how to use Jakarta Faces on Piranha Web Profile. They are: 1. Create the Maven POM file 1. Add the hellofaces.xhtml page 1. Add the HelloBean.java file 1. Add the web.xml file 1. Add the beans.xml file 1. Add an integration test 1. Test the application 1. Deploy the application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guides.webprofile faces 24.11.0-SNAPSHOT war Piranha Web Profile - Jakarta Faces application 11.0.0-M4 17 5.10.0-M1 3.11.0 3.0.0 3.3.2 webprofile 23.6.0 UTF-8 jakarta.platform jakarta.jakartaee-web-api ${jakartaee.version} provided org.junit.jupiter junit-jupiter-api ${junit.version} test org.junit.jupiter junit-jupiter-engine ${junit.version} test org.junit.jupiter junit-jupiter-params ${junit.version} test faces cloud.piranha.maven.plugins piranha-maven-plugin ${piranha.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop ${piranha.distribution} org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false ``` ## Add the Hello Faces XHTML file Add the helloworld.xhtml file in the `src/main/webapp` directory. ```html Jakarta Faces application
Jakarta Faces application

``` ## Add the HelloBean.java file Add the HelloBean.java file in the `src/main/java/hello` directory. ```java package hello; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Named; @Named(value = "helloBean") @RequestScoped public class HelloBean { private String hello = "Hello from Jakarta Faces!"; public String getHello() { return hello; } public void setHello(String hello) { this.hello = hello; } } ``` ## Add the web.xml file Add the web.xml file in the `src/main/webapp/WEB-INF` directory. ```xml Faces Servlet jakarta.faces.webapp.FacesServlet 1 Faces Servlet *.xhtml ``` ## Add the beans.xml file Add the beans.xml file in the `src/main/webapp/WEB-INF` directory. ```xml ``` ## Add an integration test As we want to make sure the application gets tested before we release an integration test is added which will be executed as part of the build. We'll add the integration test to the `src/test/java` directory. ```java package hello; import java.net.URI; import java.net.http.HttpClient; import static java.net.http.HttpClient.Redirect.ALWAYS; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import java.time.Duration; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; public class HelloIT { @Test public void testHelloFacesXhtml() throws Exception { HttpClient client = HttpClient .newBuilder() .connectTimeout(Duration.ofSeconds(60)) .followRedirects(ALWAYS) .build(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:8080/faces/hellofaces.xhtml")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello from Jakarta Faces!")); } } ``` ## Test the application The application is setup to use JUnit to do integration testing using the Piranha Maven plugin so when you are building the application it will also execute an integration test validating the web application works. To build and test the application execute the following command: ```bash mvn install ``` ## Deploy the application To deploy your application you will need 2 pieces. 1. The Piranha Web Profile runtime JAR. 2. The WAR file you just produced. For the WAR file see the `target` directory. For the Piranha Web Profile distribution go to Maven Central. And then the following command line will deploy your application: ```bash java -jar piranha-dist-webprofile.jar --war-file faces.war ``` ## Conclusion As you can integrating Jakarta Faces with Piranha Servlet can be done quickly! ================================================ FILE: dist/webprofile/src/site/markdown/create_a_hello_world_application.md ================================================ # Create a Hello World web application If you are looking to create a simple Hello World web application with Piranha Web Profile you can start here! In 5 steps you will learn how to create the web application. They are: 1. Create the Maven POM file 1. Add the helloworld.html page 1. Add an integration test 1. Test the application 1. Deploy the application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guides.webprofile helloworld 24.11.0-SNAPSHOT war Piranha Web Profile - HelloWorld application 17 5.10.0-M1 3.11.0 3.0.0 3.3.2 webprofile 23.6.0 UTF-8 org.junit.jupiter junit-jupiter-api ${junit.version} test org.junit.jupiter junit-jupiter-engine ${junit.version} test org.junit.jupiter junit-jupiter-params ${junit.version} test helloworld cloud.piranha.maven.plugins piranha-maven-plugin ${piranha.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop ${piranha.distribution} org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false ``` ## Add the Hello World HTML file Add the helloworld.html file in the `src/main/webapp` directory. ```html Hello World
Hello World!
``` ## Add an integration test As we want to make sure the application gets tested before we release an integration test is added which will be executed as part of the build. We'll add the integration test to the `src/test/java` directory. ```java package helloworld; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; class HelloWorldIT { @Test void testHelloWorldHtml() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:8080/helloworld/helloworld.html")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello World!")); } } ``` ## Test the application The application is setup to use JUnit to do integration testing using the Piranha Maven plugin so when you are building the application it will also execute an integration test validating the web application works. To build and test the application execute the following command: ```bash mvn install ``` ## Deploy the application To deploy your application you will need 2 pieces. 1. The Piranha Web Profile runtime JAR. 2. The WAR file you just produced. For the WAR file see the `target` directory. For the Piranha Web Profile distribution go to Maven Central. And then the following command line will deploy your application: ```bash java -jar piranha-dist-web-profile.jar --war-file helloworld.war ``` ## Conclusion As you can see getting started with Piranha Web Profile takes (almost) no effort! ================================================ FILE: dist/webprofile/src/site/markdown/create_a_jakarta_rest_service.md ================================================ # Create a Jakarta REST service This guide illustrates how you can create a Jakarta REST service with Piranha Web Profile. In 6 steps you will learn how to create the REST service. They are: 1. Create the Maven POM file 1. Add the application class 1. Add the endpoint 1. Add an integration test 1. Test the application 1. Deploy the application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guides.webprofile rest 24.11.0-SNAPSHOT war Piranha Web Profile - HelloREST service 11.0.0-M4 17 5.10.0-M1 3.11.0 3.0.0 3.3.2 webprofile 23.6.0 UTF-8 jakarta.platform jakarta.jakartaee-web-api ${jakartaee.version} provided org.junit.jupiter junit-jupiter-api ${junit.version} test org.junit.jupiter junit-jupiter-engine ${junit.version} test org.junit.jupiter junit-jupiter-params ${junit.version} test rest cloud.piranha.maven.plugins piranha-maven-plugin ${piranha.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop ${piranha.distribution} org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false ``` ## Add the application class Add the Application class in the `src/main/java` directory, which allows you to set the application path using the @ApplicationPath annotation. ```java package rest; import jakarta.ws.rs.ApplicationPath; import jakarta.ws.rs.core.Application; @ApplicationPath("") public class HelloRestApplication extends Application { } ``` ## Add the endpoint And we are adding a simple 'Hello REST!' endpoint that is listening on the `/hellorest` path. ```java package rest; import jakarta.enterprise.context.RequestScoped; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @RequestScoped @Path("/hellorest") public class HelloRestBean { @GET public String hello() { return "Hello REST!"; } } ``` ## Add an integration test As we want to make sure the application gets tested before we release an integration test is added which will be executed as part of the build. We'll add the integration test to the `src/test/java` directory. ```java package rest; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; class HelloRestIT { @Test void testHelloWorld() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:8080/rest/hellorest")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello REST!")); } } ``` ## Test the application The application is setup to use JUnit to do integration testing using the Piranha Maven plugin so when you are building the application it will also execute an integration test validating the endpoint works. To build and test the application execute the following command: ```bash mvn install ``` ## Deploy the application To deploy your application you will need 2 pieces. 1. The Piranha Core Profile runtime JAR. 2. The WAR file you just produced. For the WAR file see the `target` directory. For the Piranha WEb Profile distribution go to Maven Central. And then the following command line will deploy your application: ```bash java -jar piranha-dist-webprofile.jar --war-file rest.war ``` ## Conclusion As you can see getting started with Piranha Web Profile to create a REST service is pretty easy. ## References 1. [Piranha Web Profile](index.html) 1. [Piranha Maven plugin documentation](../maven-plugin/index.html) ================================================ FILE: dist/webprofile/src/site/markdown/create_a_pages_application.md ================================================ # Create a Pages application If you are looking to use Piranha Web Profile with Jakarta Page you have come to the right place! In 5 steps you will learn how to use Jakarta Pages on Piranha Web Profile. They are: 1. Create the Maven POM file 1. Add the hellopages.jsp page 1. Add an integration test 1. Test the application 1. Deploy the application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guides.webprofile pages 24.11.0-SNAPSHOT war Jakarta Pages webapplication 21 5.10.0 3.11.0 3.2.1 3.4.0 webprofile 24.10.0 UTF-8 org.junit.jupiter junit-jupiter-api ${junit.version} test org.junit.jupiter junit-jupiter-engine ${junit.version} test org.junit.jupiter junit-jupiter-params ${junit.version} test pages cloud.piranha.maven.plugins piranha-maven-plugin ${piranha.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop ${piranha.distribution} org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false ``` ## Add the Hello Pages JSP file Add the hellopages.jsp file in the `src/main/webapp` directory. ```html <%@page contentType="text/html" pageEncoding="UTF-8"%>

Hello from Jakarta Pages!

``` ## Add an integration test As we want to make sure the application gets tested before we release an integration test is added which will be executed as part of the build. We'll add the integration test to the `src/test/java/hello` directory. ```java package hello; import java.net.URI; import java.net.http.HttpClient; import static java.net.http.HttpClient.Redirect.ALWAYS; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import java.time.Duration; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; public class HelloPagesIT { @Test public void testHelloPagesJsp() throws Exception { HttpClient client = HttpClient .newBuilder() .connectTimeout(Duration.ofSeconds(60)) .followRedirects(ALWAYS) .build(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:8080/pages/hellopages.jsp")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello from Jakarta Pages!")); } } ``` ## Test the application The application is setup to use JUnit to do integration testing using the Piranha Maven plugin so when you are building the application it will also execute an integration test validating the web application works. To build and test the application execute the following command: ```bash mvn install ``` ## Deploy the application To deploy your application you will need 2 pieces. 1. The Piranha Web Profile runtime JAR. 2. The WAR file you just produced. For the WAR file see the `target` directory. For the Piranha Web Profile distribution go to Maven Central. And then the following command line will deploy your application: ```bash java -jar piranha-dist-webprofile.jar --war-file pages.war ``` ## Conclusion As you can see using Jakarta Pages on Piranha Web Profile is very easy! ================================================ FILE: dist/webprofile/src/site/markdown/index.md ================================================ # Piranha Web Profile Piranha Web Profile is our distribution that delivers Jakarta EE Web Profile support. If you are looking for Jakarta EE Web Profile support this distribution is the best match! The following list of components are available in the Piranha Web Profile distribution: 1. Jakarta Annotations 1. Jakarta Authentication 1. Jakarta Bean Validation 1. Jakarta Concurrency 1. Jakarta Contexts and Dependency Injection 1. Jakarta Debugging Support for Other Languages 1. Jakarta Dependency Injection 1. Jakarta Enterprise Beans Lite 1. Jakarta Expression Language 1. Jakarta Interceptors 1. Jakarta JSON Binding 1. Jakarta JSON Processing 1. Jakarta Persistence 1. Jakarta RESTful Web Services 1. Jakarta Security 1. Jakarta Server Faces 1. Jakarta Server Pages 1. Jakarta Servlet 1. Jakarta Standard Tag Library 1. Jakarta Transactions 1. Jakarta WebSocket ## Documentation 1. [Create a Faces application](create_a_faces_application.html) 1. [Create a Hello World application](create_a_hello_world_application.html) 1. [Create a Jakarta REST service](create_a_jakarta_rest_service.html) 1. [Create a Pages application](create_a_pages_application.html) 1. [Testing with JUnit5 and Playwright](testing_with_junit5_and_playwright.html) 1. [Testing with our container image](testing_with_our_container_image.html) ================================================ FILE: dist/webprofile/src/site/markdown/testing_with_junit5_and_playwright.md ================================================ # Testing with JUnit 5 and Playwright If you are looking to use Piranha Web Profile with JUnit 5 and Playwright then this guide will setup you up quickly! In 8 steps you will learn how to do so. They are: 1. Create the Maven POM file 1. Add the helloplaywright.xhtml page 1. Add the HelloBean.java file 1. Add the web.xml file 1. Add the beans.xml file 1. Add an integration test 1. Test the application 1. Deploy the application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.dist.webprofile.guide junit5-and-playwright 24.11.0-SNAPSHOT war Testing with JUnit 5 and Playwright 11.0.0-M4 21 5.11.3 3.13.0 3.5.2 3.4.0 webprofile 24.11.0 1.49.0 UTF-8 jakarta.platform jakarta.jakartaee-web-api ${jakartaee.version} provided org.junit.jupiter junit-jupiter-api ${junit.version} test org.junit.jupiter junit-jupiter-engine ${junit.version} test org.junit.jupiter junit-jupiter-params ${junit.version} test com.microsoft.playwright playwright ${playwright.version} test playwright cloud.piranha.maven piranha-maven-plugin ${piranha.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop ${piranha.distribution} org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false ``` ## Add the helloplaywright.xhtml file Add the helloplaywright.xhtml file in the `src/main/webapp` directory. ```html Jakarta Faces application
Jakarta Faces application

``` ## Add the HelloBean.java file Add the HelloBean.java file in the `src/main/java/hello` directory. ```java package hello; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Named; @Named(value = "helloBean") @RequestScoped public class HelloBean { private String hello = "Hello Playwright!"; public String getHello() { return hello; } public void setHello(String hello) { this.hello = hello; } } ``` ## Add the web.xml file Add the web.xml file in the `src/main/webapp/WEB-INF` directory. ```xml Faces Servlet jakarta.faces.webapp.FacesServlet 1 Faces Servlet *.xhtml ``` ## Add the beans.xml file Add the beans.xml file in the `src/main/webapp/WEB-INF` directory. ```xml ``` ## Add an integration test As we want to make sure the application gets tested before we release an integration test is added which will be executed as part of the build. We'll add the integration test to the `src/test/java` directory. ```java package hello; import com.microsoft.playwright.Browser; import com.microsoft.playwright.BrowserType; import com.microsoft.playwright.Page; import com.microsoft.playwright.Playwright; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; public class HelloIT { @Test public void testHelloPlaywrightXhtml() throws Exception { try (Playwright playwright = Playwright.create()) { BrowserType chromium = playwright.chromium(); try (Browser browser = chromium.launch()) { Page page = browser.newPage(); page.navigate("http://localhost:8080/playwright/helloplaywright.xhtml"); assertTrue(page.content().contains("Hello Playwright!")); } } } } ``` ## Test the application The application is setup to use JUnit to do integration testing using the Piranha Maven plugin so when you are building the application it will also execute an integration test validating the web application works. To build and test the application execute the following command: ```bash mvn install ``` ## Deploy the application To deploy your application you will need 2 pieces. 1. The Piranha Web Profile runtime JAR. 2. The WAR file you just produced. For the WAR file see the `target` directory. For the Piranha Web Profile distribution go to Maven Central. And then the following command line will deploy your application: ```bash java -jar piranha-dist-webprofile.jar --war-file playwright.war ``` ## Conclusion As you can see using JUnit 5 and Playwright with Piranha Web Profile is easy! ================================================ FILE: dist/webprofile/src/site/markdown/testing_with_our_container_image.md ================================================ # Testing with our container image If you are in container land this guide will show you how to use our container image for testing. In 5 steps you will learn how to it. They are: 1. Create the Maven POM file 1. Add the helloworld.html page 1. Add an integration test 1. Create the Dockerfile 1. Test the application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guides.webprofile docker 24.11.0-SNAPSHOT war Testing with our container image 11.0.0-M4 5.10.0-M1 0.43.4 3.11.0 3.0.0 3.3.2 21 UTF-8 jakarta.platform jakarta.jakartaee-web-api ${jakartaee.version} provided org.junit.jupiter junit-jupiter-api ${junit.version} test org.junit.jupiter junit-jupiter-engine ${junit.version} test org.junit.jupiter junit-jupiter-params ${junit.version} test image io.fabric8 docker-maven-plugin ${docker-maven-plugin.version} image linux/amd64 linux/arm64 ${basedir} src/main/docker/Dockerfile 8080:8080 http://localhost:8080/helloworld.html start pre-integration-test build start stop post-integration-test stop org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false ``` ## Add the Hello World HTML file Add the helloworld.html file in the `src/main/webapp` directory. ```html Hello World
Hello World!
``` ## Add an integration test As we want to make sure the application gets tested before we release an integration test is added which will be executed as part of the build. We'll add the integration test to the `src/test/java` directory. ```java package helloworld; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; class HelloWorldIT { @Test void testHelloWorldHtml() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:8080/helloworld.html")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello World!")); } } ``` ## Create the Dockerfile Create the `Dockerfile` in the `src/main/docker` directory with the following content: ```dockerfile FROM ghcr.io/piranhacloud/webprofile:23.12.0 USER piranha COPY target/image.war /home/piranha/ROOT.war CMD ["java", "-jar", "piranha-dist-webprofile.jar", "--war-file", "ROOT.war"] ``` ## Test the application The application is setup to use the Docker Maven plugin to do integration testing so when you are building the application it will also execute an integration test validating the web application works. To build and test the application execute the following command: ```bash mvn install ``` ## Conclusion As you can see getting testing with our container image is easy! ================================================ FILE: dist/webprofile/src/site/site.xml ================================================ ================================================ FILE: docker/coreprofile/pom.xml ================================================ 4.0.0 cloud.piranha.docker project 25.4.0-SNAPSHOT piranha-docker-coreprofile jar Piranha - Docker - Core Profile org.testcontainers testcontainers test default file:///tmp/piranha/docker/coreprofile/ io.fabric8 docker-maven-plugin coreprofile ghcr.io/piranhacloud/coreprofile:%l linux/amd64 linux/arm64 ${basedir} src/main/docker/Dockerfile build package build push deploy push org.apache.maven.plugins maven-dependency-plugin copy validate copy cloud.piranha.dist piranha-dist-coreprofile ${project.version} jar true ${project.build.directory} piranha-dist-coreprofile.jar org.apache.maven.plugins maven-failsafe-plugin org.apache.maven.plugins maven-javadoc-plugin true org.sonatype.plugins nexus-staging-maven-plugin true true org.apache.maven.plugins maven-project-info-reports-plugin ================================================ FILE: docker/coreprofile/src/main/docker/Dockerfile ================================================ FROM eclipse-temurin:21 RUN useradd -m piranha ADD target/piranha-dist-coreprofile.jar /home/piranha WORKDIR /home/piranha RUN chown piranha:piranha piranha-dist-coreprofile.jar USER piranha CMD ["java", "-jar", "piranha-dist-coreprofile.jar"] ================================================ FILE: docker/coreprofile/src/test/java/cloud/piranha/docker/coreprofile/CoreProfileIT.java ================================================ package cloud.piranha.docker.coreprofile; import org.junit.jupiter.api.Test; import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.DockerImageName; import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; public class CoreProfileIT { /** * Test to verify that the Docker container for Piranha Core Profile starts correctly, * maps port 8080, and responds with a status code 200 when accessed via HTTP. * * @throws Exception when a serious error occurs. */ @Test public void testBasicFunctionality() throws Exception { try (GenericContainer container = new GenericContainer<>( DockerImageName.parse("ghcr.io/piranhacloud/coreprofile:latest")) .withExposedPorts(8080)) { container.start(); Integer mappedPort = container.getMappedPort(8080); assertTrue(mappedPort != null && mappedPort > 0, "Port 8080 should be mapped"); HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("http://localhost:" + mappedPort)) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.discarding()); assertEquals(200, response.statusCode(), "Response code should be 200"); } } } ================================================ FILE: docker/pom.xml ================================================ 4.0.0 cloud.piranha project 25.4.0-SNAPSHOT cloud.piranha.docker project pom Piranha - Docker coreprofile org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test default file:///tmp/piranha/docker/ ================================================ FILE: embedded/pom.xml ================================================ 4.0.0 cloud.piranha project 25.4.0-SNAPSHOT piranha-embedded jar Piranha - Embedded cloud.piranha bom ${project.version} pom import cloud.piranha.resource piranha-resource-impl ${project.version} compile cloud.piranha.core piranha-core-impl ${project.version} compile org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test org.apache.maven.plugins maven-javadoc-plugin true org.jacoco jacoco-maven-plugin default-check check BUNDLE INSTRUCTION COVEREDRATIO 0.99 CLASS MISSEDCOUNT 0 default file:///tmp/piranha/embedded/ org.apache.maven.plugins maven-project-info-reports-plugin dependency-info ================================================ FILE: embedded/src/main/java/cloud/piranha/embedded/EmbeddedPiranha.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.embedded; import cloud.piranha.core.api.Piranha; import cloud.piranha.core.api.PiranhaConfiguration; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationRequest; import cloud.piranha.core.api.WebApplicationResponse; import cloud.piranha.core.impl.DefaultPiranhaConfiguration; import cloud.piranha.core.impl.DefaultWebApplication; import jakarta.servlet.ServletException; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; /** * The embeddable version of Piranha. * * @author Manfred Riem (mriem@manorrock.com) */ public class EmbeddedPiranha implements Piranha { /** * Stores the configuration. */ private PiranhaConfiguration configuration; /** * Stores the web application. */ private final WebApplication webApplication; /** * Constructor. */ public EmbeddedPiranha() { configuration = new DefaultPiranhaConfiguration(); webApplication = new DefaultWebApplication(); } /** * Constructor. * * @param webApplication the web application to use. */ public EmbeddedPiranha(WebApplication webApplication) { this.webApplication = webApplication; } /** * Destroy the web application. * * @return the instance. */ public EmbeddedPiranha destroy() { webApplication.destroy(); return this; } @Override public PiranhaConfiguration getConfiguration() { return configuration; } /** * {@return the web application} */ public WebApplication getWebApplication() { return webApplication; } /** * Initialize the web application. * * @return the instance. */ public EmbeddedPiranha initialize() { webApplication.initialize(); return this; } /** * This method services a request by dispatching it to the configured * Servlet and/or Filters. * * @param servletPath the request path, e.g. /foo/bar * @param parameters the request parameters, with each even parameter the * name, and odd parameter the value. e.g. /foo, 1, bar, 2 * @return the response generated by the Servlet and/or Filters * @throws IOException when an I/O error occurs. * @throws ServletException when a Servlet error occurs. */ public EmbeddedResponse service(String servletPath, String... parameters) throws IOException, ServletException { EmbeddedResponse response = new EmbeddedResponse(); EmbeddedRequest request = new EmbeddedRequestBuilder() .servletPath(servletPath) .build(); if (parameters != null && parameters.length > 0) { if (parameters.length % 2 != 0) { throw new IllegalStateException("Parameters must be provided in pairs of two"); } Map> parameterMap = new HashMap<>(); for (int i = 0; i <= parameters.length - 2; i += 2) { parameterMap.computeIfAbsent(parameters[i], e -> new ArrayList<>()).add(parameters[i + 1]); } for (Map.Entry> parameterEntry : parameterMap.entrySet()) { request.setParameter(parameterEntry.getKey(), parameterEntry.getValue().toArray(String[]::new)); } } service(request, response); return response; } /** * This method services a request by dispatching it to the configured * Servlet and/or Filters. * * @param request the request. * @return the response generated by the Servlet and/or Filters * @throws IOException when an I/O error occurs. * @throws ServletException when a Servlet error occurs. */ public EmbeddedResponse service(WebApplicationRequest request) throws IOException, ServletException { EmbeddedResponse response = new EmbeddedResponse(); service(request, response); return response; } /** * Service the request. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. * @throws ServletException when a Servlet error occurs. */ public void service(WebApplicationRequest request, WebApplicationResponse response) throws IOException, ServletException { if (request instanceof EmbeddedRequest embeddedRequest) { embeddedRequest.setWebApplication(webApplication); } if (response instanceof EmbeddedResponse embeddedResponse) { embeddedResponse.setWebApplication(webApplication); } webApplication.linkRequestAndResponse(request, response); webApplication.service(request, response); webApplication.unlinkRequestAndResponse(request, response); } /** * Start the web application. * * @return the instance. */ public EmbeddedPiranha start() { webApplication.start(); return this; } /** * Stop the web application. * * @return the instance. */ public EmbeddedPiranha stop() { webApplication.stop(); return this; } } ================================================ FILE: embedded/src/main/java/cloud/piranha/embedded/EmbeddedPiranhaBuilder.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.embedded; import cloud.piranha.core.api.HttpSessionManager; import cloud.piranha.core.api.PiranhaBuilder; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import cloud.piranha.core.impl.DefaultWebApplicationExtensionContext; import cloud.piranha.resource.api.Resource; import cloud.piranha.resource.impl.AliasedDirectoryResource; import cloud.piranha.resource.impl.DirectoryResource; import cloud.piranha.resource.impl.StringResource; import jakarta.servlet.FilterRegistration; import jakarta.servlet.ServletRegistration; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * The builder so you can easily build instances of * {@link cloud.piranha.embedded.EmbeddedPiranha}. * * @author Manfred Riem (mriem@manorrock.com) * @see cloud.piranha.embedded.EmbeddedPiranha */ public class EmbeddedPiranhaBuilder implements PiranhaBuilder { /** * Stores the async supported flags. */ private final Map asyncSupportedServlets; /** * Stores the attributes. */ private final Map attributes; /** * Stores the class loader. */ private ClassLoader classLoader; /** * Stores the extension. */ private final List> extensionClasses; /** * Stores the initializers. */ private final List initializers; /** * Stores the filters. */ private final Map filters; /** * Stores the filter init parameters map. */ private final Map> filterInitParameters; /** * Stores the filter mappings. */ private final Map> filterMappings; /** * Stores the HTTP session manager. */ private HttpSessionManager httpSessionManager; /** * Stores the listeners. */ private final ArrayList listeners; /** * Stores the resources. */ private final ArrayList resources; /** * Stores the servlets. */ private final LinkedHashMap servlets; /** * Stores the servlet init parameters map. */ private final LinkedHashMap> servletInitParameters; /** * Stores the servlet mappings. */ private final LinkedHashMap> servletMappings; /** * Stores the web application. */ private WebApplication webApplication; /** * Constructor. */ public EmbeddedPiranhaBuilder() { asyncSupportedServlets = new LinkedHashMap<>(); attributes = new LinkedHashMap<>(); extensionClasses = new ArrayList<>(); filters = new LinkedHashMap<>(); filterInitParameters = new LinkedHashMap<>(); filterMappings = new LinkedHashMap<>(); initializers = new ArrayList<>(); listeners = new ArrayList<>(); resources = new ArrayList<>(); servlets = new LinkedHashMap<>(); servletInitParameters = new LinkedHashMap<>(); servletMappings = new LinkedHashMap<>(); } /** * Add an aliased directory resource. * * @param path the path. * @param alias the alias. * @return the builder. */ public EmbeddedPiranhaBuilder aliasedDirectoryResource(String path, String alias) { resources.add(new AliasedDirectoryResource(new File(path), alias)); return this; } /** * Add an attribute. * * @param name the name. * @param value the value. * @return the builder. */ public EmbeddedPiranhaBuilder attribute(String name, Object value) { attributes.put(name, value); return this; } /** * Build the Piranha Embedded instance. * * @return the instance. */ @Override public EmbeddedPiranha build() { EmbeddedPiranha piranha; if (webApplication == null) { piranha = new EmbeddedPiranha(); webApplication = piranha.getWebApplication(); } else { piranha = new EmbeddedPiranha(webApplication); } if (classLoader != null) { webApplication.setClassLoader(classLoader); } if (extensionClasses != null && !extensionClasses.isEmpty()) { DefaultWebApplicationExtensionContext context = new DefaultWebApplicationExtensionContext(); for (Class extensionClass : extensionClasses) { context.add(extensionClass); } context.configure(webApplication); } if (httpSessionManager != null) { webApplication.getManager().setHttpSessionManager(httpSessionManager); httpSessionManager.setWebApplication(webApplication); } attributes.entrySet().forEach(attribute -> { String attributeName = attribute.getKey(); Object attributeValue = attribute.getValue(); webApplication.setAttribute(attributeName, attributeValue); }); resources.forEach(webApplication::addResource); initializers.forEach(webApplication::addInitializer); listeners.forEach(webApplication::addListener); servlets.entrySet().forEach(entry -> { String servletName = entry.getKey(); String className = entry.getValue(); ServletRegistration.Dynamic servlet = webApplication.addServlet(servletName, className); HashMap initParameters = servletInitParameters.get(servletName); if (initParameters != null) { initParameters.entrySet().forEach(initParameter -> { String name = initParameter.getKey(); String value = initParameter.getValue(); servlet.setInitParameter(name, value); }); } servlet.setAsyncSupported(asyncSupportedServlets.get(servletName)); }); servletMappings.entrySet().forEach(servletMapping -> { String servletName = servletMapping.getKey(); List urlPatterns = servletMapping.getValue(); ServletRegistration servlet = webApplication.getServletRegistration(servletName); servlet.addMapping(urlPatterns.toArray(String[]::new)); }); filters.entrySet().forEach(entry -> { String filterName = entry.getKey(); String className = entry.getValue(); FilterRegistration.Dynamic filter = webApplication.addFilter(filterName, className); HashMap initParameters = filterInitParameters.get(filterName); if (initParameters != null) { initParameters.entrySet().forEach(initParameter -> { String name = initParameter.getKey(); String value = initParameter.getValue(); filter.setInitParameter(name, value); }); } }); filterMappings.entrySet().forEach(filterMapping -> { String filterName = filterMapping.getKey(); List urlPatterns = filterMapping.getValue(); FilterRegistration filter = webApplication.getFilterRegistration(filterName); filter.addMappingForUrlPatterns(null, false, urlPatterns.toArray(String[]::new)); }); webApplication.initialize(); return piranha; } /** * Build and starts the Piranha Embedded instance. * * @return the instance. */ public EmbeddedPiranha buildAndStart() { return build() .start(); } /** * Set the class loader. * * @param classLoader the class loader. * @return the builder. */ public EmbeddedPiranhaBuilder classLoader(ClassLoader classLoader) { this.classLoader = classLoader; return this; } /** * Add a directory resource. * * @param path the path. * @return the builder. */ public EmbeddedPiranhaBuilder directoryResource(String path) { resources.add(new DirectoryResource(path)); return this; } /** * Set the web application extension. * * @param extensionClass the extension class. * @return the builder. */ public EmbeddedPiranhaBuilder extension(Class extensionClass) { extensionClasses.add(extensionClass); return this; } /** * Set the web application extensions. * * @param extensionClasses the extension classes. * @return the builder. */ @SafeVarargs public final EmbeddedPiranhaBuilder extensions(Class... extensionClasses) { for (Class extensionClass : extensionClasses) { extension(extensionClass); } return this; } /** * Add a filter. * * @param filterName the filter name. * @param filterClass the filter class. * @return the builder. */ public EmbeddedPiranhaBuilder filter(String filterName, Class filterClass) { filters.put(filterName, filterClass.getName()); return this; } /** * Add a filter. * * @param filterName the filter name. * @param className the class name. * @return the builder. */ public EmbeddedPiranhaBuilder filter(String filterName, String className) { filters.put(filterName, className); return this; } /** * Set a filter init parameter. * * @param filterName the filter name. * @param name the name. * @param value the value. * @return the builder. */ public EmbeddedPiranhaBuilder filterInitParam(String filterName, String name, String value) { HashMap initParameters = filterInitParameters.get(filterName); if (initParameters == null) { initParameters = new HashMap<>(); filterInitParameters.put(filterName, initParameters); } initParameters.put(name, value); return this; } /** * Add a filter mapping. * * @param filterName the filter name. * @param urlPatterns the URL patterns. * @return the builder. */ public EmbeddedPiranhaBuilder filterMapping(String filterName, String... urlPatterns) { filterMappings.put(filterName, Arrays.asList(urlPatterns)); return this; } /** * Set the HTTP session manager. * * @param httpSessionManager the HTTP session manager. * @return the builder. */ public EmbeddedPiranhaBuilder httpSessionManager(HttpSessionManager httpSessionManager) { this.httpSessionManager = httpSessionManager; return this; } /** * Add an initializer. * * @param initializerClass the class name. * @return the builder. */ public EmbeddedPiranhaBuilder initializer(Class initializerClass) { initializers.add(initializerClass.getName()); return this; } /** * Add an initializer. * * @param className the class name. * @return the builder. */ public EmbeddedPiranhaBuilder initializer(String className) { initializers.add(className); return this; } /** * Add initializers. * * @param initializerClasses the classes * @return the builder. */ public EmbeddedPiranhaBuilder initializers(Class... initializerClasses) { for (Class initializerClass : initializerClasses) { initializers.add(initializerClass.getName()); } return this; } /** * Add a listeners. * * @param className the class name. * @return the builder. */ public EmbeddedPiranhaBuilder listener(String className) { listeners.add(className); return this; } private EmbeddedPiranhaBuilder processServletsMapped(Object... objects) { for (int i = 0; i < objects.length; i += 2) { Class servletClass = (Class) objects[i]; String urlPattern = (String) objects[i + 1]; servlet(servletClass.getSimpleName(), servletClass.getName(), false); servletMapping(servletClass.getSimpleName(), urlPattern); } return this; } /** * Add a servlet * * @param servletName the servlet name. * @param servletClass the servlet class. * @return the builder. */ public EmbeddedPiranhaBuilder servlet(String servletName, Class servletClass) { return servlet(servletName, servletClass.getName(), false); } /** * Add a servlet. * * @param servletName the servlet name. * @param className the class name. * @return the builder. */ public EmbeddedPiranhaBuilder servlet(String servletName, String className) { return servlet(servletName, className, false); } /** * Add a servlet. * * @param servletName the servlet name. * @param servletClass the servlet class. * @param asyncSupported the async supported flag. * @return the builder. */ public EmbeddedPiranhaBuilder servlet(String servletName, Class servletClass, boolean asyncSupported) { return servlet(servletName, servletClass.getName(), asyncSupported); } /** * Add a servlet. * * @param servletName the servlet name. * @param className the class name. * @param asyncSupported the async supported flag. * @return the builder. */ public EmbeddedPiranhaBuilder servlet(String servletName, String className, boolean asyncSupported) { servlets.put(servletName, className); asyncSupportedServlets.put(servletName, asyncSupported); return this; } /** * Set a servlet init parameter. * * @param servletName the servlet name. * @param name the name. * @param value the value. * @return the builder. */ public EmbeddedPiranhaBuilder servletInitParam(String servletName, String name, String value) { HashMap initParameters = servletInitParameters.get(servletName); if (initParameters == null) { initParameters = new HashMap<>(); servletInitParameters.put(servletName, initParameters); } initParameters.put(name, value); return this; } /** * Add a servlet and a servlet mapping. * * @param servletClass the servlet class. * @param urlPatterns the URL patterns * @return the builder. */ public EmbeddedPiranhaBuilder servletMapped(Class servletClass, String... urlPatterns) { servlet(servletClass.getSimpleName(), servletClass.getName(), false); return servletMapping(servletClass.getSimpleName(), urlPatterns); } /** * Add a servlet and a servlet mapping. * * @param servletClass the servlet class. * @param asyncSupported the async supported flag. * @param urlPatterns the URL patterns * @return the builder. */ public EmbeddedPiranhaBuilder servletMapped(Class servletClass, boolean asyncSupported, String... urlPatterns) { servlet(servletClass.getSimpleName(), servletClass.getName(), asyncSupported); return servletMapping(servletClass.getSimpleName(), urlPatterns); } /** * Add a servlet mapping. * * @param servletName the servlet name. * @param urlPatterns the URL patterns. * @return the builder. */ public EmbeddedPiranhaBuilder servletMapping(String servletName, String... urlPatterns) { servletMappings.put(servletName, Arrays.asList(urlPatterns)); return this; } /** * Add servlets and their servlet mapping. * * @param servletClass1 the first servlet class. * @param urlPattern1 the first URL pattern. * @return the builder. */ public EmbeddedPiranhaBuilder servletsMapped(Class servletClass1, String urlPattern1) { return processServletsMapped( servletClass1, urlPattern1); } /** * Add servlets and their servlet mapping. * * @param servletClass1 the first servlet class. * @param urlPattern1 the first URL pattern. * @param servletClass2 the second servlet class. * @param urlPattern2 the second URL pattern. * @return the builder. */ public EmbeddedPiranhaBuilder servletsMapped(Class servletClass1, String urlPattern1, Class servletClass2, String urlPattern2) { return processServletsMapped( servletClass1, urlPattern1, servletClass2, urlPattern2); } /** * Add servlets and their servlet mapping. * * @param servletClass1 the first servlet class. * @param urlPattern1 the first URL pattern. * @param servletClass2 the second servlet class. * @param urlPattern2 the second URL pattern. * @param servletClass3 the third servlet class. * @param urlPattern3 the third URL pattern. * @return the builder. */ public EmbeddedPiranhaBuilder servletsMapped(Class servletClass1, String urlPattern1, Class servletClass2, String urlPattern2, Class servletClass3, String urlPattern3) { return processServletsMapped( servletClass1, urlPattern1, servletClass2, urlPattern2, servletClass3, urlPattern3); } /** * Add servlets and their servlet mapping. * * @param servletClass1 the first servlet class. * @param urlPattern1 the first URL pattern. * @param servletClass2 the second servlet class. * @param urlPattern2 the second URL pattern. * @param servletClass3 the third servlet class. * @param urlPattern3 the third URL pattern. * @param servletClass4 the fourth servlet class. * @param urlPattern4 the fourth URL pattern. * @return the builder. */ public EmbeddedPiranhaBuilder servletsMapped(Class servletClass1, String urlPattern1, Class servletClass2, String urlPattern2, Class servletClass3, String urlPattern3, Class servletClass4, String urlPattern4) { return processServletsMapped( servletClass1, urlPattern1, servletClass2, urlPattern2, servletClass3, urlPattern3, servletClass4, urlPattern4); } /** * Add servlets and their servlet mapping. * * @param servletClass1 the first servlet class. * @param urlPattern1 the first URL pattern. * @param servletClass2 the second servlet class. * @param urlPattern2 the second URL pattern. * @param servletClass3 the third servlet class. * @param urlPattern3 the third URL pattern. * @param servletClass4 the fourth servlet class. * @param urlPattern4 the fourth URL pattern. * @param servletClass5 the fifth servlet class. * @param urlPattern5 the fifth URL pattern. * @return the builder. */ public EmbeddedPiranhaBuilder servletsMapped(Class servletClass1, String urlPattern1, Class servletClass2, String urlPattern2, Class servletClass3, String urlPattern3, Class servletClass4, String urlPattern4, Class servletClass5, String urlPattern5) { return processServletsMapped( servletClass1, urlPattern1, servletClass2, urlPattern2, servletClass3, urlPattern3, servletClass4, urlPattern4, servletClass5, urlPattern5); } /** * Add a string resource. * * @param path the path. * @param value the string value added under the given path. * @return the builder. */ public EmbeddedPiranhaBuilder stringResource(String path, String value) { resources.add(new StringResource(path, value)); return this; } /** * Set the web application. * * @param webApplication the web application. * @return the builder. */ public EmbeddedPiranhaBuilder webApplication(WebApplication webApplication) { this.webApplication = webApplication; return this; } } ================================================ FILE: embedded/src/main/java/cloud/piranha/embedded/EmbeddedRequest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.embedded; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.impl.DefaultWebApplicationRequest; /** * The WebApplicationRequest class used by * {@link cloud.piranha.embedded.EmbeddedPiranha} * * @author Manfred Riem (mriem@manorrock.com) * @author Arjan Tijms * @see cloud.piranha.embedded.EmbeddedPiranha */ public class EmbeddedRequest extends DefaultWebApplicationRequest { /** * Another Constructor. * * @param servletPath the servletPath * */ public EmbeddedRequest(String servletPath) { this(); setServletPath(servletPath); setContextPath(""); } /** * Constructor. */ public EmbeddedRequest() { super(); this.localAddress = "127.0.0.1"; this.localName = "localhost"; this.localPort = 80; this.remoteAddr = "127.0.0.1"; this.remoteHost = "localhost"; this.remotePort = 18080; } /** * Get the web application. * * @return the web application. */ public WebApplication getWebApplication() { return webApplication; } } ================================================ FILE: embedded/src/main/java/cloud/piranha/embedded/EmbeddedRequestBuilder.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.embedded; import jakarta.servlet.http.Cookie; import cloud.piranha.core.api.WebApplication; /** * The builder so you can easily build instances of * {@link cloud.piranha.embedded.EmbeddedRequest}. * * @author Manfred Riem (mriem@manorrock.com) * @see cloud.piranha.embedded.EmbeddedRequest */ public class EmbeddedRequestBuilder { /** * Stores the request. */ private final EmbeddedRequest request; /** * Constructor. */ public EmbeddedRequestBuilder() { request = new EmbeddedRequest(); } /** * Add an attribute. * * @param name the name. * @param value the value. * @return the builder. */ public EmbeddedRequestBuilder attribute(String name, Object value) { request.setAttribute(name, value); return this; } /** * Build the request. * * @return the request. */ public EmbeddedRequest build() { if (request.getContextPath() == null) { request.setContextPath(""); } return request; } /** * Set the context path. * * @param contextPath the context path. * @return the builder. */ public EmbeddedRequestBuilder contextPath(String contextPath) { request.setContextPath(contextPath); return this; } /** * Add a cookie. * * @param cookie the cookie. * @return the builder. */ public EmbeddedRequestBuilder cookie(Cookie cookie) { Cookie[] cookies; if (request.getCookies() != null) { cookies = new Cookie[request.getCookies().length + 1]; for (int i = 0; i < cookies.length - 1; i++) { cookies[i] = (Cookie) request.getCookies()[i].clone(); } cookies[cookies.length - 1] = cookie; } else { cookies = new Cookie[1]; cookies[0] = cookie; } request.setCookies(cookies); return this; } /** * Adds a single valued header * * @param name the name of the header * @param value the value of the header * @return the builder. */ public EmbeddedRequestBuilder header(String name, String value) { request.setHeader(name, value); return this; } /** * Set the method. * * @param method the method. * @return the builder. */ public EmbeddedRequestBuilder method(String method) { request.setMethod(method); return this; } /** * Add a parameter. * * @param name the name. * @param values the values. * @return the builder. */ public EmbeddedRequestBuilder parameter(String name, String... values) { request.setParameter(name, values); return this; } /** * Set the path info. * * @param pathInfo the path info. * @return the builder. */ public EmbeddedRequestBuilder pathInfo(String pathInfo) { request.setPathInfo(pathInfo); return this; } /** * Set the requested session id. * * @param requestedSessionId the requested session id. * @return the builder. */ public EmbeddedRequestBuilder requestedSessionId(String requestedSessionId) { request.setRequestedSessionId(requestedSessionId); return this; } /** * Set the requested session id from cookie flag. * * @param requestedSessionIdFromCookie the requested session id from cookie flag. * @return the builder. */ public EmbeddedRequestBuilder requestedSessionIdFromCookie(boolean requestedSessionIdFromCookie) { request.setRequestedSessionIdFromCookie(requestedSessionIdFromCookie); return this; } /** * Set the scheme. * * @param scheme the scheme. * @return the builder. */ public EmbeddedRequestBuilder scheme(String scheme) { request.setScheme(scheme); return this; } /** * Set the servlet path. * * @param servletPath the servlet path. * @return the builder. */ public EmbeddedRequestBuilder servletPath(String servletPath) { request.setServletPath(servletPath); return this; } /** * Set the web application. * * @param webApp the web application. * @return return the builder. */ public EmbeddedRequestBuilder webApplication(WebApplication webApp) { request.setWebApplication(webApp); return this; } } ================================================ FILE: embedded/src/main/java/cloud/piranha/embedded/EmbeddedResponse.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.embedded; import cloud.piranha.core.impl.DefaultWebApplicationResponse; import cloud.piranha.core.impl.DefaultWebApplicationOutputStream; import java.io.ByteArrayOutputStream; /** * The WebApplicationResponse class used by * {@link cloud.piranha.embedded.EmbeddedPiranha} * * @author Manfred Riem (mriem@manorrock.com) * @see cloud.piranha.embedded.EmbeddedPiranha */ public class EmbeddedResponse extends DefaultWebApplicationResponse { /** * Constructor. */ public EmbeddedResponse() { this.bodyOnly = true; this.webApplicationOutputStream = new DefaultWebApplicationOutputStream(); this.webApplicationOutputStream.setResponse(this); } /** * Get the response as a byte array. * * @return the body. */ public byte[] getResponseAsByteArray() { ByteArrayOutputStream byteOutputStream = (ByteArrayOutputStream) webApplicationOutputStream.getOutputStream(); return byteOutputStream.toByteArray(); } /** * {@return the response as a string} */ public String getResponseAsString() { return new String(getResponseAsByteArray()); } } ================================================ FILE: embedded/src/main/java/cloud/piranha/embedded/EmbeddedResponseBuilder.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.embedded; import cloud.piranha.core.api.WebApplication; /** * The builder so you can easily build instances of * {@link cloud.piranha.embedded.EmbeddedResponse}. * * @author Manfred Riem (mriem@manorrock.com) * @see cloud.piranha.embedded.EmbeddedResponse */ public class EmbeddedResponseBuilder { /** * Stores the response. */ private final EmbeddedResponse response; /** * Constructor. */ public EmbeddedResponseBuilder() { response = new EmbeddedResponse(); } /** * Set the body only flag. * * @param bodyOnly the body only flag. * @return the builder. */ public EmbeddedResponseBuilder bodyOnly(boolean bodyOnly) { response.setBodyOnly(bodyOnly); return this; } /** * Build the response. * * @return the response. */ public EmbeddedResponse build() { return response; } /** * Set the web application. * * @param webApplication the web application. * @return the builder. */ public EmbeddedResponseBuilder webApplication(WebApplication webApplication) { response.setWebApplication(webApplication); return this; } } ================================================ FILE: embedded/src/main/java/cloud/piranha/embedded/package-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** *

* This package delivers you with an embeddable Servlet container that hosts * only a single application without exposing an HTTP endpoint. It is used * extensively within the Piranha project itself to test all the Servlet * functionality. *

* *

* The image below illustrates how the request and response handling is done by * Piranha Embedded. When an {@link cloud.piranha.embedded.EmbeddedRequest} * comes in it uses a * {@link cloud.piranha.core.api.WebApplicationRequestMapper} to determine * which FilterChain needs to process the incoming request. *

* *

* Embedded request and response handling *

* * @author Manfred Riem (mriem@manorrock.com) * @see cloud.piranha.embedded.EmbeddedPiranhaBuilder */ package cloud.piranha.embedded; ================================================ FILE: embedded/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers Piranha Embedded. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.embedded { exports cloud.piranha.embedded; opens cloud.piranha.embedded; requires transitive cloud.piranha.core.api; requires transitive cloud.piranha.core.impl; requires transitive jakarta.servlet; requires static java.naming; } ================================================ FILE: embedded/src/site/markdown/create_a_hello_world_web_application.md ================================================ # Create a Hello World web application If you are looking to create a simple Hello World web application with Piranha Embedded you are at the right place! In 3 steps you will learn how to create the web application. They are: 1. Create the Maven POM file 1. Add the Hello World Servlet 1. Add the Hello World Application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guides.embedded helloworld 24.11.0-SNAPSHOT jar Piranha Embedded - Create a Hello World web application 24.8.0 21 UTF-8 3.4.1 3.13.0 3.4.2 org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.codehaus.mojo exec-maven-plugin ${exec-maven-plugin.version} helloworld.HelloWorldApplication org.apache.maven.plugins maven-jar-plugin ${maven-jar-plugin.version} helloworld.HelloWorldApplication cloud.piranha piranha-embedded ${piranha.version} compile cloud.piranha.http piranha-http-impl ${piranha.version} compile cloud.piranha.http piranha-http-webapp ${piranha.version} compile ``` ## Add the Hello World Servlet Add the HelloWorldServlet.java file in the `src/main/webapp/helloworld` directory. ```java package helloworld; import java.io.IOException; import java.io.PrintWriter; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; public class HelloWorldServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try (PrintWriter writer = response.getWriter()) { writer.println("Hello World"); writer.flush(); } } } ``` ## Add the Hello World application Add the HelloWorldApplication.java file in the `src/main/webapp/helloworld` directory. ```java package helloworld; import cloud.piranha.embedded.EmbeddedPiranha; import cloud.piranha.embedded.EmbeddedPiranhaBuilder; import cloud.piranha.http.impl.DefaultHttpServer; import cloud.piranha.http.webapp.HttpWebApplicationServerProcessor; public class HelloWorldApplication { public static void main(String[] arguments) throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .servlet("HelloWorld", HelloWorldServlet.class) .servletMapping("HelloWorld", "/*") .buildAndStart(); DefaultHttpServer server = new DefaultHttpServer(8080, new HttpWebApplicationServerProcessor(piranha), false); server.start(); } } ``` ## Conclusion As you can see getting started with Piranha Embedded is easy! ================================================ FILE: embedded/src/site/markdown/create_a_piranha_embedded_graalvm_application.md ================================================ # Create a Piranha Embedded GraalVM application Can you create a GraalVM application with Piranha Embedded? In 6 steps you will learn how to so. They are: 1. Create the Maven POM file 1. Add the application class 1. Add the servlet class 1. Add the module-info 1. Build the GraalVM binary 1. Run the GraalVM binary ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guides.embedded graalvm 24.11.0-SNAPSHOT jar Piranha Embedded - Create a GraalVM application 3.1.0 17 3.11.0 3.3.0 23.6.0 UTF-8 helloworld org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.codehaus.mojo exec-maven-plugin ${exec-maven-plugin.version} helloworld.HelloWorld org.apache.maven.plugins maven-jar-plugin ${maven-jar-plugin.version} helloworld.HelloWorld cloud.piranha piranha-embedded ${piranha.version} compile cloud.piranha.http piranha-http-impl ${piranha.version} compile cloud.piranha.http piranha-http-webapp ${piranha.version} compile graalvm org.graalvm.nativeimage native-image-maven-plugin 21.2.0 native-image package false helloworld -H:Log=registerResource:5 -H:IncludeResourceBundles=jakarta.servlet.LocalStrings -H:IncludeResourceBundles=jakarta.servlet.http.LocalStrings --no-fallback --install-exit-handlers ``` ## Add the application class Add the HelloWorldApplication class in the `src/main/java` directory, which allows you to configure the EmbeddedPiranha instance as well as the DefaultHttpServer instance. ```java package helloworld; import cloud.piranha.embedded.EmbeddedPiranha; import cloud.piranha.embedded.EmbeddedPiranhaBuilder; import cloud.piranha.http.impl.DefaultHttpServer; import cloud.piranha.http.webapp.HttpWebApplicationServerProcessor; public class HelloWorldApplication { public static void main(String[] arguments) throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .servlet("HelloWorld", HelloWorldServlet.class) .servletMapping("HelloWorld", "/*") .buildAndStart(); DefaultHttpServer server = new DefaultHttpServer(8080, new HttpWebApplicationServerProcessor(piranha), false); server.start(); } } ``` ## Add the servlet And we are adding a simple HelloWorld servlet. ```java package helloworld; import java.io.IOException; import java.io.PrintWriter; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; public class HelloWorldServlet extends HttpServlet { /** * Constructor. * *

* Note the constructor is needed here to make it so we can generate the * GraalVM binary. See META-INF/native-image for more information. *

*/ public HelloWorldServlet() { } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try (PrintWriter writer = response.getWriter()) { writer.println("Hello World!"); writer.flush(); } } } ``` ## Add the module info Now add the module-info.java to the `src/main/java` directory. ``` module helloworld { exports helloworld; requires cloud.piranha.http.api; requires cloud.piranha.http.impl; requires cloud.piranha.http.webapp; requires jakarta.servlet; } ``` ## Build the application The following command line will build your application: ```bash mvn verify ``` ## Build the GraalVM binary The following command line will use Docker to build the GraalVM binary: ```bash docker build -t graalvm -f Dockerfile . ``` ## Execute the GraalVM binary The following command will use the generated Docker image to execute your GraalVM binary. ```bash docker run --rm -it -p 8080:8080 graalvm ``` ## Conclusion As you can see creating a GraalVM application with Piranha Embedded is a few easy steps away! ## References 1. [Piranha Embedded](index.html) ================================================ FILE: embedded/src/site/markdown/create_an_embedded_jlink_application.md ================================================ # Create a Piranha Embedded JLink application Can you create a JLink application with Piranha Embedded? In 6 steps you will learn how to so. They are: 1. Create the Maven POM file 1. Add the application class 1. Add the servlet class 1. Add the module-info 1. Build the application 1. Run the application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guides.embedded jlink 24.11.0-SNAPSHOT jar Piranha Embedded - JLink application 3.2.0 21 1.10.0 3.12.1 3.6.1 3.3.0 24.2.0 UTF-8 helloworld.HelloWorldApplication org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-dependency-plugin ${maven-dependency-plugin.version} copy-dependencies prepare-package copy-dependencies ${project.build.directory}/modules org.codehaus.mojo exec-maven-plugin ${exec-maven-plugin.version} ${main.class} org.apache.maven.plugins maven-jar-plugin ${maven-jar-plugin.version} ${main.class} org.jreleaser jreleaser-maven-plugin ${jrelease-maven-plugin.version} true ${project.name} Manorrock.com ALWAYS {{distributionName}}-{{projectVersion}} helloworld ${main.class} true ${project.build.directory}/${project.artifactId}-${project.version}.jar ${project.build.directory}/modules/*.jar assemble verify assemble cloud.piranha piranha-embedded ${piranha.version} compile cloud.piranha.http piranha-http-impl ${piranha.version} compile cloud.piranha.http piranha-http-webapp ${piranha.version} compile ``` ## Add the application class Add the HelloWorldApplication class in the `src/main/java` directory, which allows you to configure the EmbeddedPiranha instance as well as the DefaultHttpServer instance. ```java package helloworld; import cloud.piranha.embedded.EmbeddedPiranha; import cloud.piranha.embedded.EmbeddedPiranhaBuilder; import cloud.piranha.http.impl.DefaultHttpServer; import cloud.piranha.http.webapp.HttpWebApplicationServerProcessor; public class HelloWorldApplication { public static void main(String[] arguments) throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .servlet("HelloWorld", HelloWorldServlet.class) .servletMapping("HelloWorld", "/*") .buildAndStart(); DefaultHttpServer server = new DefaultHttpServer(8080, new HttpWebApplicationServerProcessor(piranha), false); server.start(); } } ``` ## Add the servlet And we are adding a simple HelloWorld servlet. ```java package helloworld; import java.io.IOException; import java.io.PrintWriter; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; public class HelloWorldServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try (PrintWriter writer = response.getWriter()) { writer.println("Hello World"); writer.flush(); } } } ``` ## Add the module info Now add the module-info.java to the `src/main/java` directory. ``` module helloworld { exports helloworld; requires cloud.piranha.embedded; requires cloud.piranha.http.api; requires cloud.piranha.http.impl; requires cloud.piranha.http.webapp; requires jakarta.servlet; } ``` ## Build the application The following command line will build your application: ```bash mvn verify ``` ## Run the application In the previous step you created the JLink custom image. To execute the application go to the `target/jreleaser` directory and look for the `bin` directory and execute the `helloworld` wrapper in it. And then point your browser to `http://localhost:8080` to see it in action. ## Conclusion As you can see creating a JLink runtime image with Piranha Embedded is a few easy steps away! ## References 1. [Piranha Embedded](index.html) 1. [JReleaser](https://jreleaser.org/) ================================================ FILE: embedded/src/site/markdown/index.md ================================================ # Piranha Embedded Piranha Embedded can be used to integrate Piranha into your application. ## Documentation 1. [Create a Piranha Embedded GraalVM application](create_a_piranha_embedded_graalvm_application.html) 1. [Create a Hello World web application](create_a_hello_world_web_application.html) 1. [Create an embedded JLink application](create_an_embedded_jlink_application.html) 1. [Running Piranha Embedded with Spring Boot](running_piranha_embedded_with_spring_boot.html) ================================================ FILE: embedded/src/site/markdown/running_piranha_embedded_with_spring_boot.md ================================================ # Running Piranha Embedded with Spring Boot How can you integrate Piranha Embedded with Spring Boot? In 4 steps you will learn how to so. They are: 1. Create the Maven POM file 1. Add the application class 1. Add the endpoint 1. Deploy the application ## Create the Maven POM file Create an empty directory to store your Maven project. Inside of that directory create the ```pom.xml``` file with the content as below. ```xml 4.0.0 cloud.piranha.guides.embedded springboot 24.11.0-SNAPSHOT jar Piranha Embedded - Running Piranha Embedded with Spring Boot 21 3.11.0 24.8.0 3.3.2 UTF-8 org.springframework.boot spring-boot-dependencies ${spring-boot.version} pom import org.springframework.boot spring-boot-starter-web compile org.springframework.boot spring-boot-starter-tomcat cloud.piranha.spring piranha-embedded-spring-boot-starter ${piranha.version} compile org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.springframework.boot spring-boot-maven-plugin ${spring-boot.version} package repackage ``` ## Add the application class Add the Application class in the `src/main/java` directory, which allows you to configure the ServletWebServerFactory to be used. ```java package hello; import cloud.piranha.spring.starter.embedded.EmbeddedPiranhaServletWebServerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.context.annotation.Bean; @SpringBootApplication public class HelloApplication { @Bean public ServletWebServerFactory factory() { return new EmbeddedPiranhaServletWebServerFactory(); } @Bean public WebServerFactoryCustomizer customizer() { return new WebServerFactoryCustomizer() { @Override public void customize(EmbeddedPiranhaServletWebServerFactory factory) { factory.setPort(8080); } }; } public static void main(String[] arguments) { SpringApplication.run(HelloApplication.class, arguments); } } ``` ## Add the endpoint And we are adding a simple 'Hello' endpoint that is listening on the `/hello` path. ```java package hello; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/hello") public String hello() { return "Hello"; } } ``` ## Deploy the application The following command line will deploy your application: ```bash java -jar springboot.jar ``` ## Conclusion As you can see Piranha Embedded and Spring Boot can work together. ## References 1. [Piranha Embedded](index.html) ================================================ FILE: embedded/src/site/site.xml ================================================ ================================================ FILE: embedded/src/test/java/cloud/piranha/embedded/EmbeddedPiranhaBuilderTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.embedded; import cloud.piranha.core.api.ServletEnvironment; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import cloud.piranha.core.impl.DefaultHttpSessionManager; import cloud.piranha.core.impl.DefaultWebApplication; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletRequestEvent; import jakarta.servlet.ServletRequestListener; import jakarta.servlet.ServletResponse; import jakarta.servlet.http.HttpServlet; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the EmbeddedPiranhaBuilder class. * * @author Manfred Riem (mriem@manorrock.com) */ class EmbeddedPiranhaBuilderTest { /** * Test aliasedDirectoryResource method. * * @throws Exception when a serious error occurs. */ @Test void testAliasedDirectoryResource() throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .aliasedDirectoryResource(".", "/myalias") .build(); assertNotNull(piranha.getWebApplication().getResource("/myalias/pom.xml")); } /** * Test attribute method. */ @Test void testAttribute() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .attribute("name", "value") .build(); assertEquals("value", piranha.getWebApplication().getAttribute("name")); } /** * Test directoryResource method. * * @throws Exception when a serious error occurs. */ @Test void testDirectoryResource() throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .directoryResource(".") .build(); assertNotNull(piranha.getWebApplication().getResource("/pom.xml")); } /** * Test classLoader method. * * @throws Exception when a serious error occurs. */ @Test void testClassLoader() throws Exception { URLClassLoader classLoader = new URLClassLoader(new URL[]{}); EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .classLoader(classLoader) .build(); assertEquals(classLoader, piranha.getWebApplication().getClassLoader()); } /** * Test extension method. */ @Test void testExtension() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .extension(TestExtension.class) .buildAndStart(); assertEquals("value", piranha.getWebApplication().getAttribute("name")); } /** * Test extension method. */ @Test void testExtensions() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .extensions(TestExtension.class) .buildAndStart(); assertEquals("value", piranha.getWebApplication().getAttribute("name")); } /** * Test filter method. */ @Test void testFilter() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .filter("filter", TestFilter.class) .build(); assertNotNull(piranha.getWebApplication().getFilterRegistration("filter")); } /** * Test filter method. */ @Test void testFilter2() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .filter("filter", TestFilter.class.getName()) .build(); assertNotNull(piranha.getWebApplication().getFilterRegistration("filter")); } /** * Test filterInitParam method. */ @Test void testFilterInitParam() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .filter("filter", TestFilter.class) .filterInitParam("filter", "name", "value") .build(); assertEquals("value", piranha.getWebApplication(). getFilterRegistration("filter").getInitParameter("name")); } /** * Test filterMapping method. */ @Test void testFilterMapping() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .filter("filter", TestFilter.class) .filterMapping("filter", "/filter") .build(); assertEquals("/filter", piranha.getWebApplication() .getFilterRegistration("filter").getUrlPatternMappings() .iterator().next()); } /** * Test httpSessionManager method. */ @Test void testHttpSessionManager() { DefaultHttpSessionManager httpSessionManager = new DefaultHttpSessionManager(); EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .httpSessionManager(httpSessionManager) .build(); assertEquals(httpSessionManager, piranha.getWebApplication() .getManager().getHttpSessionManager()); } /** * Test initializer method. */ @Test void testInitializer() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .initializer(TestInitializer.class) .buildAndStart(); assertTrue((boolean) piranha.getWebApplication().getAttribute("initialized")); } /** * Test initializer method. */ @Test void testInitializer2() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .initializer(TestInitializer.class.getName()) .buildAndStart(); assertTrue((boolean) piranha.getWebApplication().getAttribute("initialized")); } /** * Test initializers method. */ @Test void testInitializers() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .initializers(TestInitializer.class) .buildAndStart(); assertTrue((boolean) piranha.getWebApplication().getAttribute("initialized")); } /** * Test listener method. * * @throws Exception when an error occurs. */ @Test void testListener() throws Exception { EmbeddedPiranhaBuilder builder = new EmbeddedPiranhaBuilder(); EmbeddedPiranha piranha = builder .listener(TestServletRequestListener.class.getName()) .build() .start(); EmbeddedRequest request = new EmbeddedRequest(); EmbeddedResponse response = new EmbeddedResponse(); piranha.service(request, response); assertTrue((Boolean) piranha.getWebApplication().getAttribute("requestInitialized")); } /** * Test servlet method. */ @Test void testServlet() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .servlet("servlet", TestServlet.class) .build(); assertNotNull(piranha.getWebApplication().getServletRegistration("servlet")); } /** * Test servlet method. */ @Test void testServlet2() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .servlet("servlet", TestServlet.class.getName()) .build(); assertNotNull(piranha.getWebApplication().getServletRegistration("servlet")); } /** * Test servlet method. */ @Test void testServlet3() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .servlet("servlet", TestServlet.class, true) .build(); assertNotNull(piranha.getWebApplication().getServletRegistration("servlet")); ServletEnvironment servletEnvironment = (ServletEnvironment) piranha .getWebApplication().getServletRegistration("servlet"); assertTrue(servletEnvironment.isAsyncSupported()); } /** * Test servlet method. */ @Test void testServlet4() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .servlet("servlet", TestServlet.class.getName(), true) .build(); assertNotNull(piranha.getWebApplication().getServletRegistration("servlet")); ServletEnvironment servletEnvironment = (ServletEnvironment) piranha .getWebApplication().getServletRegistration("servlet"); assertTrue(servletEnvironment.isAsyncSupported()); } /** * Test servletInitParam method. */ @Test void testServletInitParam() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .servlet("servlet", TestServlet.class) .servletInitParam("servlet", "name", "value") .build(); assertEquals("value", piranha.getWebApplication(). getServletRegistration("servlet").getInitParameter("name")); } /** * Test servletMapping method. */ @Test void testServletMapping() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .servlet("servlet", TestServlet.class) .servletMapping("servlet", "/servlet") .build(); assertEquals("/servlet", piranha.getWebApplication() .getServletRegistration("servlet").getMappings().iterator() .next()); } /** * Test servletMapped method. */ @Test void testServletMapped() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .servletMapped(TestServlet.class, "/servlet") .buildAndStart(); assertEquals("/servlet", piranha.getWebApplication() .getServletRegistration("TestServlet").getMappings().iterator() .next()); } /** * Test servletMapped method. */ @Test void testServletMapped2() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .servletMapped(TestServlet.class, true, "/servlet") .buildAndStart(); assertNotNull(piranha.getWebApplication().getServletRegistration("TestServlet")); ServletEnvironment servletEnvironment = (ServletEnvironment) piranha .getWebApplication().getServletRegistration("TestServlet"); assertTrue(servletEnvironment.isAsyncSupported()); } /** * Test servletsMapped method. */ @Test void testServletsMapped() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .servletsMapped(TestServlet.class, "/servlet") .buildAndStart(); assertEquals("/servlet", piranha.getWebApplication() .getServletRegistration("TestServlet").getMappings().iterator() .next()); } /** * Test servletsMapped method. */ @Test void testServletsMapped2() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .servletsMapped(TestServlet.class, "/servlet", Test2Servlet.class, "/servlet2") .buildAndStart(); assertEquals("/servlet", piranha.getWebApplication() .getServletRegistration("TestServlet").getMappings().iterator() .next()); assertEquals("/servlet2", piranha.getWebApplication() .getServletRegistration("Test2Servlet").getMappings().iterator() .next()); } /** * Test servletsMapped method. */ @Test void testServletsMapped3() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .servletsMapped( TestServlet.class, "/servlet", Test2Servlet.class, "/servlet2", Test3Servlet.class, "/servlet3") .buildAndStart(); assertEquals("/servlet", piranha.getWebApplication() .getServletRegistration("TestServlet").getMappings().iterator() .next()); assertEquals("/servlet2", piranha.getWebApplication() .getServletRegistration("Test2Servlet").getMappings().iterator() .next()); assertEquals("/servlet3", piranha.getWebApplication() .getServletRegistration("Test3Servlet").getMappings().iterator() .next()); } /** * Test servletsMapped method. */ @Test void testServletsMapped4() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .servletsMapped( TestServlet.class, "/servlet", Test2Servlet.class, "/servlet2", Test3Servlet.class, "/servlet3", Test4Servlet.class, "/servlet4") .buildAndStart(); assertEquals("/servlet", piranha.getWebApplication() .getServletRegistration("TestServlet").getMappings().iterator() .next()); assertEquals("/servlet2", piranha.getWebApplication() .getServletRegistration("Test2Servlet").getMappings().iterator() .next()); assertEquals("/servlet3", piranha.getWebApplication() .getServletRegistration("Test3Servlet").getMappings().iterator() .next()); assertEquals("/servlet4", piranha.getWebApplication() .getServletRegistration("Test4Servlet").getMappings().iterator() .next()); } /** * Test servletsMapped method. */ @Test void testServletsMapped5() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .servletsMapped( TestServlet.class, "/servlet", Test2Servlet.class, "/servlet2", Test3Servlet.class, "/servlet3", Test4Servlet.class, "/servlet4", Test5Servlet.class, "/servlet5") .buildAndStart(); assertEquals("/servlet", piranha.getWebApplication() .getServletRegistration("TestServlet").getMappings().iterator() .next()); assertEquals("/servlet2", piranha.getWebApplication() .getServletRegistration("Test2Servlet").getMappings().iterator() .next()); assertEquals("/servlet3", piranha.getWebApplication() .getServletRegistration("Test3Servlet").getMappings().iterator() .next()); assertEquals("/servlet4", piranha.getWebApplication() .getServletRegistration("Test4Servlet").getMappings().iterator() .next()); assertEquals("/servlet5", piranha.getWebApplication() .getServletRegistration("Test5Servlet").getMappings().iterator() .next()); } /** * Test stringResource method. * * @throws Exception when a serious error occurs. */ @Test void testStringResource() throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .stringResource("/textfile.txt", "This is text") .build(); assertNotNull(piranha.getWebApplication().getResource("/textfile.txt")); } /** * Test webApplication method. */ @Test void testWebApplication() { DefaultWebApplication webApplication = new DefaultWebApplication(); EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .webApplication(webApplication) .build(); assertEquals(webApplication, piranha.getWebApplication()); } /** * A test extension used to test the extension method. */ public static class TestExtension implements WebApplicationExtension { @Override public void configure(WebApplication webApplication) { webApplication.setAttribute("name", "value"); } } /** * A test filter used to test the filter method. */ public static class TestFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { } } /** * A test initializer used to test the initializer method. */ public static class TestInitializer implements ServletContainerInitializer { @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { servletContext.setAttribute("initialized", true); } } /** * A test servlet used to test various servlet related methods. */ public static class TestServlet extends HttpServlet { } /** * A test servlet used to test the servletsMapped method. */ public static class Test2Servlet extends HttpServlet { } /** * A test servlet used to test the servletsMapped method. */ public static class Test3Servlet extends HttpServlet { } /** * A test servlet used to test the servletsMapped method. */ public static class Test4Servlet extends HttpServlet { } /** * A test servlet used to test the servletsMapped method. */ public static class Test5Servlet extends HttpServlet { } /** * A test ServletRequestListener used to test the listener method. */ public static class TestServletRequestListener implements ServletRequestListener { public TestServletRequestListener() { } @Override public void requestInitialized(ServletRequestEvent event) { event.getServletContext().setAttribute("requestInitialized", true); } } } ================================================ FILE: embedded/src/test/java/cloud/piranha/embedded/EmbeddedPiranhaTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.embedded; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.Map.Entry; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; /** * The JUnit tests for the EmbeddedPiranha class. * * @author Manfred Riem (mriem@manorrock.com) */ class EmbeddedPiranhaTest { /** * Test extension method. * * @throws Exception when a serious error occurs. */ @Test void testExtension() throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .extension(TestExtension.class) .buildAndStart(); assertNotNull(piranha.getWebApplication().getAttribute(TestInitializer.class.getName())); } /** * Test service method. */ @Test void testService() { try { EmbeddedRequest request = new EmbeddedRequest(); EmbeddedResponse response = new EmbeddedResponse(); EmbeddedPiranha piranha = new EmbeddedPiranha(); piranha.initialize(); piranha.start(); piranha.service(request, response); piranha.stop(); } catch (IOException | ServletException ex) { fail(); } } /** * Test service method. */ @Test void testService2() { try { EmbeddedRequest request = new EmbeddedRequest(); EmbeddedPiranha piranha = new EmbeddedPiranha(); piranha.initialize(); piranha.start(); piranha.service(request); piranha.stop(); piranha.destroy(); } catch (IOException | ServletException ex) { fail(); } } /** * Test service method. */ @Test void testService3() { try { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .servlet("TestServlet", TestServlet.class) .servletMapping("TestServlet", "/servletPath") .buildAndStart(); EmbeddedResponse response = piranha.service("/servletPath"); piranha.stop(); piranha.destroy(); assertTrue(response.getResponseAsString().contains("/servletPath")); } catch (IOException | ServletException ex) { fail(); } } /** * Test service method. */ @Test void testService4() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .servlet("TestServlet", TestServlet.class) .servletMapping("TestServlet", "/servletPath") .buildAndStart(); try { piranha.service("/servletPath", "wrong"); } catch (IOException | ServletException ex) { fail(); } catch (IllegalStateException ise) { } piranha.stop(); piranha.destroy(); } /** * Test service method. */ @Test void testService5() { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .servlet("TestServlet", TestServlet.class) .servletMapping("TestServlet", "/servletPath") .buildAndStart(); try { EmbeddedResponse response = piranha.service("/servletPath", "name", "value"); assertTrue(response.getResponseAsString().contains("/servletPath")); assertTrue(response.getResponseAsString().contains("name: value")); } catch (IOException | ServletException ex) { fail(); } catch (IllegalStateException ise) { } piranha.stop(); piranha.destroy(); } /** * A test extension. */ public static class TestExtension implements WebApplicationExtension { /** * Configure the web application. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { webApplication.addInitializer(TestInitializer.class.getName()); } } /** * A test ServletContainerInitializer. */ public static class TestInitializer implements ServletContainerInitializer { /** * On startup. * * @param classes the list of annotated classes. * @param servletContext the Servlet context. * @throws ServletException when a Servlet error occurs. */ @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { servletContext.setAttribute(TestInitializer.class.getName(), true); } } /** * A test Servlet. */ public static class TestServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("text/plain"); PrintWriter writer = response.getWriter(); writer.println(request.getServletPath()); for (Entry en : request.getParameterMap().entrySet()) { String key = en.getKey(); String[] values = en.getValue(); writer.println(key + ": " + values[0]); } } } } ================================================ FILE: embedded/src/test/java/cloud/piranha/embedded/EmbeddedRequestBuilderTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.embedded; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.impl.DefaultWebApplication; import jakarta.servlet.http.Cookie; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * The JUnit tests for the EmbeddedRequestBuilder class. * * @author Manfred Riem (mriem@manorrock.com) */ class EmbeddedRequestBuilderTest { /** * Test attribute method. */ @Test void testAttribute() { EmbeddedRequest request = new EmbeddedRequestBuilder() .attribute("name", "value") .build(); assertEquals("value", request.getAttribute("name")); } /** * Test contextPath method. */ @Test void testContextPath() { EmbeddedRequest request = new EmbeddedRequestBuilder() .contextPath("/mycontext") .build(); assertEquals("/mycontext", request.getContextPath()); } /** * Test contextPath method. */ @Test void testContextPath2() { EmbeddedRequest request = new EmbeddedRequestBuilder() .contextPath(null) .build(); assertEquals("", request.getContextPath()); } /** * Test cookie method. */ @Test void testCookie() { EmbeddedRequest request = new EmbeddedRequestBuilder() .cookie(new Cookie("name", "value")) .cookie(new Cookie("name2", "value2")) .build(); assertEquals("name", request.getCookies()[0].getName()); assertEquals("value", request.getCookies()[0].getValue()); assertEquals("name2", request.getCookies()[1].getName()); assertEquals("value2", request.getCookies()[1].getValue()); } /** * Test header method. */ @Test void testHeader() { EmbeddedRequest request = new EmbeddedRequestBuilder() .header("name", "value") .build(); assertEquals("value", request.getHeader("name")); } /** * Test method method. */ @Test void testMethod() { EmbeddedRequest request = new EmbeddedRequestBuilder() .method("POST") .build(); assertEquals("POST", request.getMethod()); } /** * Test parameter method. */ @Test void testParameter() { EmbeddedRequest request = new EmbeddedRequestBuilder() .parameter("name", "value") .build(); assertEquals("value", request.getParameter("name")); } /** * Test parameter method. */ @Test void testParameter2() { EmbeddedRequest request = new EmbeddedRequestBuilder() .parameter("name", "value1", "value2") .build(); assertEquals("value2", request.getParameterValues("name")[1]); } /** * Test pathInfo method. */ @Test void testPathInfo() { EmbeddedRequest request = new EmbeddedRequestBuilder() .build(); assertNull(request.getPathInfo()); } /** * Test pathInfo method. */ @Test void testPathInfo2() { EmbeddedRequest request = new EmbeddedRequestBuilder() .pathInfo("/pathInfo") .build(); assertEquals("/pathInfo", request.getPathInfo()); } /** * Test requestedSessionId method. */ @Test void testRequestedSessionId() { EmbeddedRequest request = new EmbeddedRequestBuilder() .requestedSessionId("does-not-exist") .build(); assertEquals("does-not-exist", request.getRequestedSessionId()); } /** * Test requestedSessionIdFromCookie method. */ @Test void testRequestedSessionIdFromCookie() { EmbeddedRequest request = new EmbeddedRequestBuilder() .requestedSessionIdFromCookie(true) .build(); assertTrue(request.isRequestedSessionIdFromCookie()); assertFalse(request.isRequestedSessionIdFromURL()); } /** * Test scheme method. */ @Test void testScheme() { EmbeddedRequest request = new EmbeddedRequestBuilder() .scheme("https") .build(); assertTrue(request.isSecure()); assertEquals("https", request.getScheme()); } /** * Test servletPath method. */ @Test void testServletPath() { EmbeddedRequest request = new EmbeddedRequestBuilder() .servletPath("/servletPath") .build(); assertEquals("/servletPath", request.getServletPath()); } /** * Test webApplication method. * * @throws Exception when an error occurs. */ @Test void testWebApplication() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); EmbeddedRequest request = new EmbeddedRequestBuilder() .webApplication(webApplication) .build(); assertTrue(request.getWebApplication() instanceof WebApplication); assertEquals(webApplication, request.getWebApplication()); } } ================================================ FILE: embedded/src/test/java/cloud/piranha/embedded/EmbeddedRequestTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.embedded; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * The JUnit tests for the EmbeddedRequest class. * * @author Manfred Riem (mriem@manorrock.com) */ class EmbeddedRequestTest { /** * Test constructor. */ @Test void testConstructor() { EmbeddedRequest request = new EmbeddedRequest("/servletPath"); assertEquals("/servletPath", request.getServletPath()); } } ================================================ FILE: embedded/src/test/java/cloud/piranha/embedded/EmbeddedResponseBuilderTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.embedded; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.impl.DefaultWebApplication; import java.io.ByteArrayOutputStream; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; /** * The JUnit tests for the EmbeddedResponseBuilder class. * * @author Manfred Riem (mriem@manorrock.com) */ class EmbeddedResponseBuilderTest { /** * Test bodyOnly method. * * @throws Exception when an error occurs. */ @Test void testBodyOnly() throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder().build(); EmbeddedRequest request = new EmbeddedRequestBuilder().build(); request.setWebApplication(piranha.getWebApplication()); EmbeddedResponse response = new EmbeddedResponseBuilder() .bodyOnly(true) .build(); response.setWebApplication(piranha.getWebApplication()); piranha.getWebApplication().linkRequestAndResponse(request, response); response.getWebApplicationOutputStream().setOutputStream(new ByteArrayOutputStream()); response.addHeader("header", "not there"); response.flushBuffer(); assertFalse(response.getResponseAsString().contains("not there")); } /** * Test bodyOnly method. * * @throws Exception when an error occurs. */ @Test void testBodyOnly2() throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder().build(); EmbeddedRequest request = new EmbeddedRequestBuilder().build(); request.setWebApplication(piranha.getWebApplication()); EmbeddedResponse response = new EmbeddedResponseBuilder() .bodyOnly(false) .build(); response.setWebApplication(piranha.getWebApplication()); piranha.getWebApplication().linkRequestAndResponse(request, response); response.getWebApplicationOutputStream().setOutputStream(new ByteArrayOutputStream()); response.addHeader("header", "there"); response.flushBuffer(); assertTrue(response.getResponseAsString().contains("header: there")); } /** * Test webApplication method. * * @throws Exception when an error occurs. */ @Test void testWebApplication() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); EmbeddedResponse response = new EmbeddedResponseBuilder() .webApplication(webApplication) .build(); assertTrue(response.getWebApplication() instanceof WebApplication); assertEquals(webApplication, response.getWebApplication()); } } ================================================ FILE: extension/angus/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-angus jar Piranha - Extension - Angus cloud.piranha.core piranha-core-api ${project.version} compile cloud.piranha.extension piranha-extension-scinitializer ${project.version} provided org.eclipse.angus angus-mail runtime org.eclipse.angus angus-activation runtime default file:///tmp/piranha/extension/angus/ ================================================ FILE: extension/angus/src/main/java/cloud/piranha/extension/angus/AngusExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.angus; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import java.lang.System.Logger; import static java.lang.System.Logger.Level.TRACE; /** * The extension that delivers Eclipse Angus Mail to Piranha. * * @author Manfred Riem (mriem@manorrock.com) */ public class AngusExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(AngusExtension.class.getName()); /** * Constructor. */ public AngusExtension() { } /** * Configure the extension. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { LOGGER.log(TRACE, "Configuring Angus extension"); } } ================================================ FILE: extension/angus/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Eclipse Angus integration extension. * *

* This extension integrates Eclipse Angus Mail into Piranha. *

* * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.angus { requires cloud.piranha.core.api; requires static cloud.piranha.extension.scinitializer; } ================================================ FILE: extension/annotationscan/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-annotationscan jar Piranha - Extension - Annotation Scan cloud.piranha.core piranha-core-impl ${project.version} compile cloud.piranha.resource piranha-resource-api ${project.version} compile org.junit.jupiter junit-jupiter-api test org.junit.platform junit-platform-launcher test default file:///tmp/piranha/extension/annotationscan/ org.apache.maven.plugins maven-surefire-plugin @{argLine} --add-opens cloud.piranha.extension.annotationscan/cloud.piranha.extension.annotationscan.internal=ALL-UNNAMED ================================================ FILE: extension/annotationscan/src/main/java/cloud/piranha/extension/annotationscan/AnnotationScanExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.annotationscan; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import jakarta.servlet.ServletContainerInitializer; import java.lang.System.Logger; import java.lang.System.Logger.Level; import java.lang.reflect.InvocationTargetException; /** * The extension that enables annotation scanning. * * @author Manfred Riem (mriem@manorrock.com) */ public class AnnotationScanExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(AnnotationScanExtension.class.getName()); /** * Constructor. */ public AnnotationScanExtension() { } /** * Configure the web application. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { try { ClassLoader classLoader = webApplication.getClassLoader(); Class clazz = classLoader .loadClass(AnnotationScanInitializer.class.getName()) .asSubclass(ServletContainerInitializer.class); ServletContainerInitializer initializer = clazz.getDeclaredConstructor().newInstance(); webApplication.addInitializer(initializer); } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { LOGGER.log(Level.WARNING, "Unable to enable AnnotationScanExtension", ex); } } } ================================================ FILE: extension/annotationscan/src/main/java/cloud/piranha/extension/annotationscan/AnnotationScanInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.annotationscan; import cloud.piranha.extension.annotationscan.internal.InternalAnnotationScanAnnotationManager; import cloud.piranha.extension.annotationscan.internal.InternalAnnotationScanAnnotationInfo; import cloud.piranha.resource.api.ResourceManagerClassLoader; import cloud.piranha.core.api.AnnotationManager; import cloud.piranha.core.api.WebApplication; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.MultipartConfig; import jakarta.servlet.annotation.ServletSecurity; import jakarta.servlet.annotation.WebFilter; import jakarta.servlet.annotation.WebInitParam; import jakarta.servlet.annotation.WebListener; import jakarta.servlet.annotation.WebServlet; import java.lang.System.Logger; import static java.lang.System.Logger.Level.TRACE; import static java.lang.System.Logger.Level.WARNING; import java.lang.annotation.Annotation; import static java.util.Arrays.stream; import java.util.Set; import java.util.stream.Stream; /** * This ServletContainerInitializer deep scans for annotations and adds them to * the StandardAnnotationScanAnnotationManager. * * @author Arjan Tijms * @author Manfred Riem (mriem@manorrock.com) */ public class AnnotationScanInitializer implements ServletContainerInitializer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(AnnotationScanInitializer.class.getName()); /** * Constructor. */ public AnnotationScanInitializer() { } /** * On startup. * * @param classes the classes. * @param servletContext the servlet context. * @throws ServletException when a servlet error occurs. */ @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { WebApplication webApp = (WebApplication) servletContext; AnnotationManager annotationManager = webApp.getManager().getAnnotationManager(); if (annotationManager == null) { annotationManager = new InternalAnnotationScanAnnotationManager(); webApp.getManager().setAnnotationManager(annotationManager); } final AnnotationManager annotationMgr = annotationManager; ClassLoader classLoader = webApp.getClassLoader(); if (!(classLoader instanceof ResourceManagerClassLoader resourceManagerClassLoader)) { LOGGER.log(WARNING, "ResourceManagerClassLoader not installed. This scanner does not work"); return; } if (servletContext.getInitParameter("cloud.piranha.extension.annotationscan.AnnotatedClasses") != null) { String[] classNames = servletContext.getInitParameter("cloud.piranha.extension.annotationscan.AnnotatedClasses").split(","); if (classNames.length > 0) { for (String className : classNames) { Class clazz = null; try { clazz = classLoader.loadClass(className); } catch (ClassNotFoundException cnfe) { LOGGER.log(WARNING, "Unable to load class: " + className); } if (clazz != null && hasWebAnnotation(clazz)) { final Class targetClazz = clazz; getWebAnnotations(clazz).forEach( annotationInstance -> annotationMgr.addAnnotation(new InternalAnnotationScanAnnotationInfo<>(annotationInstance, targetClazz))); } } } } else { resourceManagerClassLoader .getResourceManager() .getAllLocations() .filter(e -> e.endsWith(".class") && !e.endsWith("module-info.class") && !e.startsWith("/META-INF/versions")) .map(e -> loadClass(classLoader, e)) .filter(this::hasWebAnnotation) .forEach(targetClazz -> getWebAnnotations(targetClazz) .forEach(annotationInstance -> annotationMgr.addAnnotation(new InternalAnnotationScanAnnotationInfo<>(annotationInstance, targetClazz)))); } } /** * Load the class using the given class loader. * * @param classLoader the class loader. * @param className the class name. * @return the class. */ public Class loadClass(ClassLoader classLoader, String className) { try { return classLoader.loadClass( className.replace("/", ".") .substring(1, className.length() - ".class".length())); } catch (ClassNotFoundException | NoClassDefFoundError e) { LOGGER.log(TRACE,"Unable to load class {0}, because of {1}", className, e.getMessage()); } return Object.class; } /** * Get the web annotations for the given class. * * @param clazz the class. * @return the stream of web annotations. */ private Stream getWebAnnotations(Class clazz) { return stream(clazz.getAnnotations()) .filter(this::isWebAnnotation); } /** * Does the given class have any web annotations. * * @param clazz the class. * @return true if it does, false otherwise. */ private boolean hasWebAnnotation(Class clazz) { return getWebAnnotations(clazz) .findAny() .isPresent(); } /** * Is this a web annotation. * * @param annotation the annotation. * @return true if it is, false otherwise. */ private boolean isWebAnnotation(Annotation annotation) { return annotation instanceof WebServlet || annotation instanceof WebListener || annotation instanceof WebInitParam || annotation instanceof WebFilter || annotation instanceof ServletSecurity || annotation instanceof MultipartConfig || (annotation != null && annotation.toString().contains("jakarta.ws.rs.") || (annotation != null && annotation.toString().contains("jakarta.websocket."))); } } ================================================ FILE: extension/annotationscan/src/main/java/cloud/piranha/extension/annotationscan/internal/InternalAnnotationScanAnnotationInfo.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.annotationscan.internal; import cloud.piranha.core.api.AnnotationInfo; import java.lang.reflect.AnnotatedElement; /** * The standard annotation scan AnnotationInfo. * * @author Arjan Tijms * @author Manfred Riem (mriem@manorrock.com) * @param the type. */ public class InternalAnnotationScanAnnotationInfo implements AnnotationInfo { /** * Stores the instance. */ private final T instance; /** * Stores the target. */ private final AnnotatedElement target; /** * Constructor. * * @param instance the instance. * @param target the target annotated element. */ public InternalAnnotationScanAnnotationInfo(T instance, AnnotatedElement target) { this.instance = instance; this.target = target; } @Override public T getInstance() { return instance; } @Override public AnnotatedElement getTarget() { return target; } } ================================================ FILE: extension/annotationscan/src/main/java/cloud/piranha/extension/annotationscan/internal/InternalAnnotationScanAnnotationManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.annotationscan.internal; import cloud.piranha.core.api.AnnotationInfo; import cloud.piranha.core.api.AnnotationManager; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import static java.util.Collections.emptyList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import static java.util.stream.Collectors.toList; import java.util.stream.Stream; /** * The standard annotation scan AnnotationManager. * * @author Manfred Riem (mriem@manorrock.com) */ public class InternalAnnotationScanAnnotationManager implements AnnotationManager { /** * Stores the annotations. */ protected final Map, List>> annotations = new ConcurrentHashMap<>(); /** * Stores the instances. */ protected final Map, List>> instances = new ConcurrentHashMap<>(); /** * Constructor. */ public InternalAnnotationScanAnnotationManager() { } @Override public void addAnnotation(AnnotationInfo annotationInfo) { annotations.computeIfAbsent( ((Annotation) annotationInfo.getInstance()).annotationType(), e -> new ArrayList<>()) .add(annotationInfo); } /** * Add an instance. * * @param instanceClass the instance class. * @param implementingClass the implementing class. */ @Override public void addInstance(Class instanceClass, Class implementingClass) { instances.computeIfAbsent( instanceClass, e -> new ArrayList<>()) .add(implementingClass); } @SuppressWarnings("unchecked") private Stream> getAnnotationStream(Class annotationClass) { return annotations.getOrDefault(annotationClass, emptyList()) .stream() .map(e -> (AnnotationInfo) e); } @Override public List> getAnnotations(Class... annotationClasses) { return Arrays.stream(annotationClasses) .flatMap(this::getAnnotationStream) .collect(toList()); } @Override public List> getAnnotations(Class annotationClass) { return getAnnotationStream(annotationClass).toList(); } @Override public List> getAnnotationsByTarget( Class annotationClass, AnnotatedElement type) { return null; } @SuppressWarnings("unchecked") private Stream> getInstanceStream(Class instanceClass) { return instances.getOrDefault(instanceClass, emptyList()) .stream() .map(e -> (Class) e); } @Override public List> getInstances(Class... instanceClasses) { return Arrays.stream(instanceClasses) .flatMap(this::getInstanceStream) .collect(toList()); } @Override public List> getInstances(Class instanceClass) { return getInstanceStream(instanceClass).toList(); } @Override public void addAnnotatedClass(Class annotationClass, Class clazz) { } @Override public Set> getAnnotatedClass(Class annotationClass) { return Collections.emptySet(); } @Override public Set> getAnnotatedClasses(Class[] annotationClasses) { return Collections.emptySet(); } } ================================================ FILE: extension/annotationscan/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the annotation scan extension. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.annotationscan { exports cloud.piranha.extension.annotationscan; opens cloud.piranha.extension.annotationscan; // // The following 2 are temporarily exported/opened until we resolve its // usage in Piranha - Micro - Shrinkwrap - Core. // exports cloud.piranha.extension.annotationscan.internal; opens cloud.piranha.extension.annotationscan.internal; requires cloud.piranha.core.api; requires cloud.piranha.core.impl; } ================================================ FILE: extension/annotationscan/src/test/java/cloud/piranha/extension/annotationscan/AnnotationScanExtensionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.annotationscan; import cloud.piranha.core.api.AnnotationManager; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.extension.annotationscan.AnnotationScanExtension; import cloud.piranha.resource.impl.DefaultResourceManager; import cloud.piranha.resource.impl.DefaultResourceManagerClassLoader; import jakarta.servlet.annotation.WebServlet; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import org.junit.jupiter.api.Test; /** * The JUnit tests for the AnnotationScanExtension class. * * @author Manfred Riem (mriem@manorrock.com) */ class AnnotationScanExtensionTest { /** * Test configure method. */ @Test void testConfigure() { WebApplication webApplication = new DefaultWebApplication(); DefaultResourceManager resourceManager = new DefaultResourceManager(); DefaultResourceManagerClassLoader classLoader = new DefaultResourceManagerClassLoader(resourceManager); classLoader.setDelegateClassLoader(getClass().getClassLoader()); webApplication.setClassLoader(classLoader); AnnotationScanExtension extension = new AnnotationScanExtension(); extension.configure(webApplication); webApplication.initialize(); assertEquals(classLoader, webApplication.getClassLoader()); } /** * Test configure method. */ @Test void testConfigure2() { WebApplication webApplication = new DefaultWebApplication(); DefaultResourceManager resourceManager = new DefaultResourceManager(); DefaultResourceManagerClassLoader classLoader = new DefaultResourceManagerClassLoader(resourceManager); classLoader.setDelegateClassLoader(getClass().getClassLoader()); webApplication.setClassLoader(classLoader); webApplication.setInitParameter( "cloud.piranha.extension.annotationscan.AnnotatedClasses", "cloud.piranha.extension.annotationscan.TestServlet"); AnnotationScanExtension extension = new AnnotationScanExtension(); extension.configure(webApplication); webApplication.initialize(); assertEquals(classLoader, webApplication.getClassLoader()); AnnotationManager annotationManager = webApplication.getManager().getAnnotationManager(); assertFalse(annotationManager.getAnnotations(WebServlet.class).isEmpty()); } } ================================================ FILE: extension/annotationscan/src/test/java/cloud/piranha/extension/annotationscan/TestAnnotation.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.annotationscan; import java.lang.annotation.Retention; import static java.lang.annotation.RetentionPolicy.RUNTIME; /** * A test annotation. * * @author Manfred Riem (mriem@manorrock.com) */ @Retention(RUNTIME) public @interface TestAnnotation { } ================================================ FILE: extension/annotationscan/src/test/java/cloud/piranha/extension/annotationscan/TestServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.annotationscan; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; /** * A test Servlet. * * @author manfred */ @WebServlet(urlPatterns = {"/test"}) public class TestServlet extends HttpServlet { } ================================================ FILE: extension/annotationscan/src/test/java/cloud/piranha/extension/annotationscan/TestWithHandlesTypesInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.annotationscan; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.HandlesTypes; import java.util.Optional; import java.util.Set; /** * A test ServletContainerInitializer with HandlesTypes annotation. * * @author Manfred Riem (mriem@manorrock.com) */ @HandlesTypes({Set.class, TestAnnotation.class}) public class TestWithHandlesTypesInitializer implements ServletContainerInitializer { /** * Constructor. */ public TestWithHandlesTypesInitializer() { } @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { Optional> classInstance = classes.stream().filter(Set.class::isAssignableFrom).findFirst(); servletContext.setAttribute("object_class", classInstance.isPresent()); Optional> classWithAnnotation = classes.stream().filter(x -> x.getAnnotation(TestAnnotation.class) != null).findFirst(); servletContext.setAttribute("someannotation_class", classWithAnnotation.isPresent()); } } ================================================ FILE: extension/annotationscan/src/test/java/cloud/piranha/extension/annotationscan/internal/InternalAnnotationScanAnnotationManagerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.annotationscan.internal; import cloud.piranha.extension.annotationscan.TestWithHandlesTypesInitializer; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.extension.annotationscan.TestAnnotation; import java.util.Collections; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the StandardAnnotationScanAnnotationManager class. * * @author Manfred Riem (mriem@manorrock.com) */ class InternalAnnotationScanAnnotationManagerTest { @Test void testInitializerWithHandlesTypes () { DefaultWebApplication webApp = new DefaultWebApplication(); webApp.getManager().setAnnotationManager(new InternalAnnotationScanAnnotationManager()); webApp.addInitializer(TestWithHandlesTypesInitializer.class.getName()); webApp.initialize(); assertTrue(webApp.getAttribute("object_class") instanceof Boolean); assertFalse((Boolean) webApp.getAttribute("object_class")); assertTrue(webApp.getAttribute("someannotation_class") instanceof Boolean); assertFalse((Boolean) webApp.getAttribute("someannotation_class")); } @Test void testInitializerWithHandlesTypes2 () { DefaultWebApplication webApp = new DefaultWebApplication(); webApp.getManager().setAnnotationManager(new InternalAnnotationScanAnnotationManager()); webApp.addInitializer(TestWithHandlesTypesInitializer.class.getName()); InternalAnnotationScanAnnotationManager annotationManager = (InternalAnnotationScanAnnotationManager) webApp.getManager().getAnnotationManager(); annotationManager.addInstance(Set.class, Collections.emptySet().getClass()); annotationManager.addAnnotation(new InternalAnnotationScanAnnotationInfo<>( ClassAnnotated.class.getAnnotation(TestAnnotation.class), ClassAnnotated.class ) ); webApp.initialize(); assertTrue(webApp.getAttribute("object_class") instanceof Boolean); assertTrue((Boolean) webApp.getAttribute("object_class")); assertTrue(webApp.getAttribute("someannotation_class") instanceof Boolean); assertTrue((Boolean) webApp.getAttribute("someannotation_class")); } @TestAnnotation static class ClassAnnotated { } } ================================================ FILE: extension/annotationscan-classfile/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-annotationscan-classfile jar Piranha - Extension - Annotation Scan Classfile cloud.piranha.core piranha-core-impl ${project.version} compile cloud.piranha.resource piranha-resource-api ${project.version} compile org.junit.jupiter junit-jupiter-api test default file:///tmp/piranha/extension/annotationscanclassfile/ org.apache.maven.plugins maven-surefire-plugin ${argLine} --add-exports=java.base/jdk.internal.classfile=cloud.piranha.extension.annotationscan.classfile --add-exports=java.base/jdk.internal.classfile.attribute=cloud.piranha.extension.annotationscan.classfile ================================================ FILE: extension/annotationscan-classfile/src/main/java/cloud/piranha/extension/annotationscan/classfile/ClassfileAnnotationScanExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.annotationscan.classfile; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; /** * The extension that enables annotation scanning. * * @author Manfred Riem (mriem@manorrock.com) */ public class ClassfileAnnotationScanExtension implements WebApplicationExtension { /** * Stores the property to enable this experimental extension */ public static final String EXPERIMENTAL_PROPERTY = "cloud.piranha.extension.annotationscan.classfile.experimental"; /** * Default constructor */ public ClassfileAnnotationScanExtension() { } /** * Configure the web application. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { webApplication.addInitializer(new ClassfileAnnotationScanInitializer()); } } ================================================ FILE: extension/annotationscan-classfile/src/main/java/cloud/piranha/extension/annotationscan/classfile/ClassfileAnnotationScanInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.annotationscan.classfile; import cloud.piranha.core.api.AnnotationManager; import cloud.piranha.core.api.WebApplication; import cloud.piranha.extension.annotationscan.classfile.internal.InternalAnnotationScanAnnotationManager; import cloud.piranha.extension.annotationscan.classfile.internal.InternalAnnotationScanAnnotationInfo; import cloud.piranha.resource.api.ResourceManager; import cloud.piranha.resource.api.ResourceManagerClassLoader; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import sun.reflect.ReflectionFactory; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; import java.lang.System.Logger; import java.lang.annotation.Annotation; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDescs; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.reflect.Constructor; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Stream; import static java.lang.System.Logger.Level.ERROR; import static java.lang.System.Logger.Level.WARNING; import static java.lang.invoke.MethodType.methodType; import static java.util.Arrays.stream; /** * This ServletContainerInitializer deep scans for annotations and adds them to * the StandardAnnotationScanAnnotationManager. * * @author Arjan Tijms * @author Manfred Riem (mriem@manorrock.com) * @author Thiago Henrique Hupner */ public class ClassfileAnnotationScanInitializer implements ServletContainerInitializer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(ClassfileAnnotationScanInitializer.class.getName()); /** * Default constructor */ public ClassfileAnnotationScanInitializer() { } /** * On startup. * * @param classes the classes. * @param servletContext the servlet context. * @throws ServletException when a servlet error occurs. */ @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { WebApplication webApp = (WebApplication) servletContext; AnnotationManager annotationManager = webApp.getManager().getAnnotationManager(); if (annotationManager == null) { annotationManager = new InternalAnnotationScanAnnotationManager(); webApp.getManager().setAnnotationManager(annotationManager); } ClassLoader classLoader = webApp.getClassLoader(); if (!(classLoader instanceof ResourceManagerClassLoader resourceManagerClassLoader)) { LOGGER.log(WARNING, "ResourceManagerClassLoader not installed. This scanner does not work"); return; } ResourceManager resourceManager = resourceManagerClassLoader.getResourceManager(); resourceManager .getAllLocations() .filter(e -> e.endsWith(".class") && !e.endsWith("module-info.class") && !e.startsWith("/META-INF/versions")) .filter(resource -> classFileHasJakartaAnnotations(readResource(resource, resourceManager))) .map(e -> loadClass(classLoader, e)) .flatMap(this::getJakartaAnnotations) .map(annotationInstance -> new InternalAnnotationScanAnnotationInfo<>(annotationInstance, annotationInstance.annotationType())) .forEach(annotationManager::addAnnotation); } /** * Load the class using the given class loader. * * @param classLoader the class loader. * @param className the class name. * @return the class. */ private Class loadClass(ClassLoader classLoader, String className) { try { return classLoader.loadClass( className.replace("/", ".") .substring(1, className.length() - ".class".length())); } catch (ClassNotFoundException e) { return Object.class; } } /** * Get the web annotations for the given class. * * @param clazz the class. * @return the stream of web annotations. */ private Stream getJakartaAnnotations(Class clazz) { return stream(clazz.getAnnotations()) .filter(this::isJakartaAnnotation); } private boolean isJakartaAnnotation(Annotation clazz) { return isJakartaAnnotation(clazz.annotationType().describeConstable().orElse(ConstantDescs.CD_Object)); } private boolean isJakartaAnnotation(ClassDesc annotation) { return annotation.packageName().startsWith("jakarta"); } private byte[] readResource(String resourceName, ResourceManager resourceManager) { try (InputStream resourceAsStream = resourceManager.getResourceAsStream(resourceName)) { return resourceAsStream.readAllBytes(); } catch (IOException e) { throw new UncheckedIOException(e); } } private boolean classFileHasJakartaAnnotations(byte[] classFileBytes) { /* * Without reflection the code would be similar to: * {@snippet lang="java" * ClassModel classModel = Classfile.parse(classFileBytes); * Optional> attribute = classModel.findAttribute(Attributes.RUNTIME_VISIBLE_ANNOTATIONS); * return optionalAttribute * .stream() * .flatMap(attribute -> attribute.annotations().stream()) * .map(annotation -> annotation.classSymbol()) * .anyMatch(this::isJakartaAnnotation) * } */ try { Object classModel = Holder.PARSE.invokeExact(classFileBytes); Optional optionalAttribute = (Optional) Holder.FIND_ATTRIBUTE.invokeExact(classModel, Holder.RUNTIME_VISIBLE_ANNOTATIONS_ATTRIBUTE_MAPPER); if (optionalAttribute.isEmpty()) { return false; } List annotations = (List) Holder.ANNOTATIONS.invokeExact(optionalAttribute.get()); for (Object annotation : annotations) { ClassDesc annotationDesc = (ClassDesc) Holder.CLASS_SYMBOL.invokeExact(annotation); if (isJakartaAnnotation(annotationDesc)) { return true; } } } catch (Throwable ignored) { // invokeExact throws Throwable... } return false; } } class Holder { /** * Stores the handle to Classfile.parse(byte[]) */ static final MethodHandle PARSE; /** * Stores the handle to ClassModel.findAttribute(AttributeMapper) */ static final MethodHandle FIND_ATTRIBUTE; /** * Stores the handle to RuntimeVisibleAnnotationsAttribute.annotations() */ static final MethodHandle ANNOTATIONS; /** * Stores the handle to Annotation.classSymbol() */ static final MethodHandle CLASS_SYMBOL; /** * Stores the RuntimeVisibleAnnotationsAttributeMapper */ static final Object RUNTIME_VISIBLE_ANNOTATIONS_ATTRIBUTE_MAPPER; /** * Stores the value of trusted lookup */ private static final int TRUSTED = -1; /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(ClassfileAnnotationScanInitializer.class.getName()); private Holder() { } static { try { MethodHandles.Lookup lookup = getLookup(); Class classFileClass = lookup.findClass("jdk.internal.classfile.Classfile"); Class classModelClass = lookup.findClass("jdk.internal.classfile.ClassModel"); Class attributeMapperClass = lookup.findClass("jdk.internal.classfile.AttributeMapper"); Class attributesClass = lookup.findClass("jdk.internal.classfile.Attributes"); Class annotationClass = lookup.findClass("jdk.internal.classfile.Annotation"); Class classFileOptionsArrayClass = lookup.findClass("[Ljdk.internal.classfile.Classfile$Option;"); Class runtimeVisibleAnnotationsAttributeClass = lookup.findClass("jdk.internal.classfile.attribute.RuntimeVisibleAnnotationsAttribute"); MethodHandle parse = lookup.findStatic(classFileClass, "parse", methodType(classModelClass, byte[].class, classFileOptionsArrayClass)); Object classFileOptionsEmptyArray = MethodHandles.arrayConstructor(classFileOptionsArrayClass).invoke(0); PARSE = MethodHandles.insertArguments(parse, 1, classFileOptionsEmptyArray) .asType(methodType(Object.class, byte[].class)); FIND_ATTRIBUTE = lookup.findVirtual(classModelClass, "findAttribute", methodType(Optional.class, attributeMapperClass)) .asType(methodType(Optional.class, Object.class, Object.class)); ANNOTATIONS = lookup.findVirtual(runtimeVisibleAnnotationsAttributeClass, "annotations", methodType(List.class)) .asType(methodType(List.class, Object.class)); CLASS_SYMBOL = lookup.findVirtual(annotationClass, "classSymbol", methodType(ClassDesc.class)) .asType(methodType(ClassDesc.class, Object.class)); RUNTIME_VISIBLE_ANNOTATIONS_ATTRIBUTE_MAPPER = lookup.findStaticGetter(attributesClass, "RUNTIME_VISIBLE_ANNOTATIONS", attributeMapperClass).invoke(); } catch (Throwable e) { throw new ExceptionInInitializerError(e); } } @SuppressWarnings("deprecation") private static MethodHandles.Lookup getLookup() { MethodHandles.Lookup lookup = MethodHandles.lookup(); try { lookup.findClass("jdk.internal.classfile.Classfile"); lookup.findClass("jdk.internal.classfile.attribute.RuntimeVisibleAnnotationsAttribute"); return lookup; } catch (ClassNotFoundException | IllegalAccessException e) { Module module = lookup.lookupClass().getModule(); String moduleName = module.isNamed() ? module.getName() : "ALL-UNNAMED"; LOGGER.log(WARNING, "The classfile annotation scan extension requires the following flags to work: " + "--add-exports=java.base/jdk.internal.classfile={0} --add-exports=java.base/jdk.internal.classfile.attribute={0}. " + "Using non-standard APIs as fallback for now.", moduleName); } try { ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory(); Class lookupClass = MethodHandles.Lookup.class; Constructor constructor = lookupClass.getDeclaredConstructor(Class.class, Class.class, int.class); Constructor lookupConstructor = reflectionFactory.newConstructorForSerialization(lookupClass, constructor); return (MethodHandles.Lookup) lookupConstructor.newInstance(Object.class, null, TRUSTED); } catch (Throwable e) { LOGGER.log(ERROR, "Could not use the non-standard APIs, this extension will fail. Please add the flags to the command line."); throw new ExceptionInInitializerError(e); } } } ================================================ FILE: extension/annotationscan-classfile/src/main/java/cloud/piranha/extension/annotationscan/classfile/internal/InternalAnnotationScanAnnotationInfo.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.annotationscan.classfile.internal; import cloud.piranha.core.api.AnnotationInfo; import java.lang.reflect.AnnotatedElement; /** * The standard annotation scan AnnotationInfo. * * @author Arjan Tijms * @author Manfred Riem (mriem@manorrock.com) * @param the type. */ public class InternalAnnotationScanAnnotationInfo implements AnnotationInfo { /** * Stores the instance. */ private final T instance; /** * Stores the target. */ private final AnnotatedElement target; /** * Constructor. * * @param instance the instance. * @param target the target annotated element. */ public InternalAnnotationScanAnnotationInfo(T instance, AnnotatedElement target) { this.instance = instance; this.target = target; } @Override public T getInstance() { return instance; } @Override public AnnotatedElement getTarget() { return target; } } ================================================ FILE: extension/annotationscan-classfile/src/main/java/cloud/piranha/extension/annotationscan/classfile/internal/InternalAnnotationScanAnnotationManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.annotationscan.classfile.internal; import cloud.piranha.core.api.AnnotationInfo; import cloud.piranha.core.api.AnnotationManager; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import static java.util.Collections.emptyList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import static java.util.stream.Collectors.toList; import java.util.stream.Stream; /** * The standard annotation scan AnnotationManager. * * @author Manfred Riem (mriem@manorrock.com) */ public class InternalAnnotationScanAnnotationManager implements AnnotationManager { /** * Stores the annotations. */ protected final Map, List>> annotations = new ConcurrentHashMap<>(); /** * Stores the instances. */ protected final Map, List>> instances = new ConcurrentHashMap<>(); /** * Default constructor */ public InternalAnnotationScanAnnotationManager() { } @Override public void addAnnotation(AnnotationInfo annotationInfo) { annotations.computeIfAbsent( ((Annotation) annotationInfo.getInstance()).annotationType(), e -> new ArrayList<>()) .add(annotationInfo); } /** * Add an instance. * * @param instanceClass the instance class. * @param implementingClass the implementing class. */ @Override public void addInstance(Class instanceClass, Class implementingClass) { instances.computeIfAbsent( instanceClass, e -> new ArrayList<>()) .add(implementingClass); } @SuppressWarnings("unchecked") private Stream> getAnnotationStream(Class annotationClass) { return annotations.getOrDefault(annotationClass, emptyList()) .stream() .map(e -> (AnnotationInfo) e); } @Override public List> getAnnotations(Class... annotationClasses) { return Arrays.stream(annotationClasses) .flatMap(this::getAnnotationStream) .collect(toList()); } @Override public List> getAnnotations(Class annotationClass) { return getAnnotationStream(annotationClass).toList(); } @Override public List> getAnnotationsByTarget( Class annotationClass, AnnotatedElement type) { return null; } @SuppressWarnings("unchecked") private Stream> getInstanceStream(Class instanceClass) { return instances.getOrDefault(instanceClass, emptyList()) .stream() .map(e -> (Class) e); } @Override public List> getInstances(Class... instanceClasses) { return Arrays.stream(instanceClasses) .flatMap(this::getInstanceStream) .collect(toList()); } @Override public List> getInstances(Class instanceClass) { return getInstanceStream(instanceClass).toList(); } @Override public void addAnnotatedClass(Class annotationClass, Class clazz) { } @Override public Set> getAnnotatedClass(Class annotationClass) { return Collections.emptySet(); } @Override public Set> getAnnotatedClasses(Class[] annotationClasses) { return Collections.emptySet(); } } ================================================ FILE: extension/annotationscan-classfile/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the annotation scan extension using the Classfile API. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.annotationscan.classfile { exports cloud.piranha.extension.annotationscan.classfile; opens cloud.piranha.extension.annotationscan.classfile; exports cloud.piranha.extension.annotationscan.classfile.internal; opens cloud.piranha.extension.annotationscan.classfile.internal; requires cloud.piranha.core.api; requires cloud.piranha.core.impl; requires jdk.unsupported; } ================================================ FILE: extension/annotationscan-classfile/src/test/java/cloud/piranha/extension/annotationscan/classfile/AnnotationScanExtensionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.annotationscan.classfile; import cloud.piranha.core.api.AnnotationManager; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.resource.impl.DefaultResourceManager; import cloud.piranha.resource.impl.DefaultResourceManagerClassLoader; import cloud.piranha.resource.impl.DirectoryResource; import jakarta.servlet.annotation.WebServlet; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledOnJre; import org.junit.jupiter.api.condition.JRE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; /** * The JUnit tests for the AnnotationScanExtension class. * * @author Manfred Riem (mriem@manorrock.com) */ class AnnotationScanExtensionTest { /** * Test configure method. */ @Test void testConfigure() { WebApplication webApplication = new DefaultWebApplication(); DefaultResourceManager resourceManager = new DefaultResourceManager(); DefaultResourceManagerClassLoader classLoader = new DefaultResourceManagerClassLoader(resourceManager); classLoader.setDelegateClassLoader(getClass().getClassLoader()); webApplication.setClassLoader(classLoader); ClassfileAnnotationScanExtension extension = new ClassfileAnnotationScanExtension(); extension.configure(webApplication); webApplication.initialize(); assertEquals(classLoader, webApplication.getClassLoader()); } /** * Test configure method. */ @Test @EnabledOnJre(value = JRE.JAVA_21, disabledReason = "Only JDK 21 includes the Classfile API") void testConfigure2() { DefaultResourceManager resourceManager = new DefaultResourceManager(); resourceManager.addResource(new DirectoryResource("target/test-classes")); DefaultResourceManagerClassLoader classLoader = new DefaultResourceManagerClassLoader(getClass().getClassLoader(), resourceManager); WebApplication webApplication = new DefaultWebApplication(); webApplication.setClassLoader(classLoader); ClassfileAnnotationScanExtension extension = new ClassfileAnnotationScanExtension(); extension.configure(webApplication); webApplication.initialize(); AnnotationManager annotationManager = webApplication.getManager().getAnnotationManager(); assertFalse(annotationManager.getAnnotations(WebServlet.class).isEmpty()); } } ================================================ FILE: extension/annotationscan-classfile/src/test/java/cloud/piranha/extension/annotationscan/classfile/TestAnnotation.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.annotationscan.classfile; import java.lang.annotation.Retention; import static java.lang.annotation.RetentionPolicy.RUNTIME; /** * A test annotation. * * @author Manfred Riem (mriem@manorrock.com) */ @Retention(RUNTIME) public @interface TestAnnotation { } ================================================ FILE: extension/annotationscan-classfile/src/test/java/cloud/piranha/extension/annotationscan/classfile/TestServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.annotationscan.classfile; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; /** * A test Servlet. * * @author manfred */ @WebServlet(urlPatterns = {"/test"}) public class TestServlet extends HttpServlet { } ================================================ FILE: extension/annotationscan-classfile/src/test/java/cloud/piranha/extension/annotationscan/classfile/TestWithHandlesTypesInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.annotationscan.classfile; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.HandlesTypes; import java.util.Optional; import java.util.Set; /** * A test ServletContainerInitializer with HandlesTypes annotation. * * @author Manfred Riem (mriem@manorrock.com) */ @HandlesTypes({Set.class, TestAnnotation.class}) public class TestWithHandlesTypesInitializer implements ServletContainerInitializer { /** * Constructor. */ public TestWithHandlesTypesInitializer() { } @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { Optional> classInstance = classes.stream().filter(Set.class::isAssignableFrom).findFirst(); servletContext.setAttribute("object_class", classInstance.isPresent()); Optional> classWithAnnotation = classes.stream().filter(x -> x.getAnnotation(TestAnnotation.class) != null).findFirst(); servletContext.setAttribute("someannotation_class", classWithAnnotation.isPresent()); } } ================================================ FILE: extension/annotationscan-classfile/src/test/java/cloud/piranha/extension/annotationscan/classfile/internal/InternalAnnotationScanAnnotationManagerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.annotationscan.classfile.internal; import cloud.piranha.extension.annotationscan.classfile.TestWithHandlesTypesInitializer; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.extension.annotationscan.classfile.TestAnnotation; import java.util.Collections; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the StandardAnnotationScanAnnotationManager class. * * @author Manfred Riem (mriem@manorrock.com) */ class InternalAnnotationScanAnnotationManagerTest { @Test void testInitializerWithHandlesTypes () { DefaultWebApplication webApp = new DefaultWebApplication(); webApp.getManager().setAnnotationManager(new InternalAnnotationScanAnnotationManager()); webApp.addInitializer(TestWithHandlesTypesInitializer.class.getName()); webApp.initialize(); assertTrue(webApp.getAttribute("object_class") instanceof Boolean); assertFalse((Boolean) webApp.getAttribute("object_class")); assertTrue(webApp.getAttribute("someannotation_class") instanceof Boolean); assertFalse((Boolean) webApp.getAttribute("someannotation_class")); } @Test void testInitializerWithHandlesTypes2 () { DefaultWebApplication webApp = new DefaultWebApplication(); InternalAnnotationScanAnnotationManager annotationManager = new InternalAnnotationScanAnnotationManager(); webApp.getManager().setAnnotationManager(annotationManager); webApp.addInitializer(TestWithHandlesTypesInitializer.class.getName()); annotationManager.addInstance(Set.class, Collections.emptySet().getClass()); annotationManager.addAnnotation(new InternalAnnotationScanAnnotationInfo<>( ClassAnnotated.class.getAnnotation(TestAnnotation.class), ClassAnnotated.class ) ); webApp.initialize(); assertTrue(webApp.getAttribute("object_class") instanceof Boolean); assertTrue((Boolean) webApp.getAttribute("object_class")); assertTrue(webApp.getAttribute("someannotation_class") instanceof Boolean); assertTrue((Boolean) webApp.getAttribute("someannotation_class")); } @TestAnnotation static class ClassAnnotated { } } ================================================ FILE: extension/bytesstreamhandler/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-bytesstreamhandler jar Piranha - Extension - Bytes Stream Handler cloud.piranha.resource piranha-resource-impl ${project.version} compile cloud.piranha.core piranha-core-api ${project.version} provided cloud.piranha piranha-embedded ${project.version} test org.junit.jupiter junit-jupiter-api test default file:///tmp/piranha/extension/bytestreamhandler/ ================================================ FILE: extension/bytesstreamhandler/src/main/java/cloud/piranha/extension/bytesstreamhandler/BytesStreamHandlerExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.bytesstreamhandler; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import cloud.piranha.resource.impl.ByteArrayResourceStreamHandlerProvider; import static java.lang.System.Logger.Level.DEBUG; /** * The WebApplicationExtension that sets up the 'bytes://' stream handler * functionality. * * @author Manfred Riem (mriem@manorrock.com) */ public class BytesStreamHandlerExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(BytesStreamHandlerExtension.class.getName()); /** * Constructor. */ public BytesStreamHandlerExtension() { } @Override public void configure(WebApplication webApplication) { LOGGER.log(DEBUG, "Configuring 'bytes://' stream handler functionality"); ByteArrayResourceStreamHandlerProvider.setGetResourceAsStreamFunction( webApplication::getResourceAsStream); webApplication.addListener(BytesStreamHandlerServletContextListener.class.getName()); webApplication.addListener(BytesStreamHandlerServletRequestListener.class.getName()); } } ================================================ FILE: extension/bytesstreamhandler/src/main/java/cloud/piranha/extension/bytesstreamhandler/BytesStreamHandlerServletContextListener.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.bytesstreamhandler; import cloud.piranha.resource.impl.ByteArrayResourceStreamHandlerProvider; import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletContextListener; import static java.lang.System.Logger.Level.DEBUG; /** * The ServletContextListener used to remove 'bytes://' stream handler once * initialization is done. * * @author Manfred Riem (mriem@manorrock.com) */ public class BytesStreamHandlerServletContextListener implements ServletContextListener { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(BytesStreamHandlerServletContextListener.class.getName()); /** * Constructor. */ public BytesStreamHandlerServletContextListener() { } @Override public void contextInitialized(ServletContextEvent event) { LOGGER.log(DEBUG, "Removing bytes:// stream handler"); ByteArrayResourceStreamHandlerProvider.setGetResourceAsStreamFunction(null); } } ================================================ FILE: extension/bytesstreamhandler/src/main/java/cloud/piranha/extension/bytesstreamhandler/BytesStreamHandlerServletRequestListener.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.bytesstreamhandler; import cloud.piranha.core.api.WebApplication; import cloud.piranha.resource.impl.ByteArrayResourceStreamHandlerProvider; import jakarta.servlet.ServletRequestEvent; import jakarta.servlet.ServletRequestListener; import static java.lang.System.Logger.Level.DEBUG; /** * The ServletRequestListener that sets up 'bytes://' stream handler when the * request is initialized and removes it when the request is destroyed. * * @author Manfred Riem (mriem@manorrock.com) */ public class BytesStreamHandlerServletRequestListener implements ServletRequestListener { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(BytesStreamHandlerServletRequestListener.class.getName()); /** * Constructor. */ public BytesStreamHandlerServletRequestListener() { } @Override public void requestDestroyed(ServletRequestEvent event) { LOGGER.log(DEBUG, "Removing bytes:// stream handler"); ByteArrayResourceStreamHandlerProvider.setGetResourceAsStreamFunction(null); } @Override public void requestInitialized(ServletRequestEvent event) { LOGGER.log(DEBUG, "Setting up bytes:// stream handler"); WebApplication webApplication = (WebApplication) event.getServletContext(); ByteArrayResourceStreamHandlerProvider.setGetResourceAsStreamFunction( webApplication::getResourceAsStream); } } ================================================ FILE: extension/bytesstreamhandler/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the bytes:// stream handler extension. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.bytesstreamhandler { exports cloud.piranha.extension.bytesstreamhandler; opens cloud.piranha.extension.bytesstreamhandler; requires transitive cloud.piranha.core.api; requires cloud.piranha.resource.impl; } ================================================ FILE: extension/bytesstreamhandler/src/test/java/cloud/piranha/extension/bytesstreamhandler/BytesStreamHandlerExtensionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.bytesstreamhandler; import cloud.piranha.embedded.EmbeddedPiranha; import cloud.piranha.embedded.EmbeddedPiranhaBuilder; import cloud.piranha.embedded.EmbeddedRequest; import cloud.piranha.embedded.EmbeddedResponse; import cloud.piranha.resource.impl.ByteArrayResourceStreamHandlerProvider; import jakarta.servlet.ServletRequestEvent; import jakarta.servlet.ServletRequestListener; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the ByteArrayExtension class. * * @author Manfred Riem (mriem@manorrock.com) */ class BytesStreamHandlerExtensionTest { /** * Test configure method. * * @throws Exception when a serious error occurs. */ @Test void testConfigure() throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .extension(BytesStreamHandlerExtension.class) .listener(TestServletRequestListener.class.getName()) .build() .start(); EmbeddedRequest request = new EmbeddedRequest(); EmbeddedResponse response = new EmbeddedResponse(); piranha.service(request, response); assertTrue((Boolean) piranha.getWebApplication().getAttribute("found")); } public static class TestServletRequestListener implements ServletRequestListener { /** * Constructor. */ public TestServletRequestListener() { } @Override public void requestInitialized(ServletRequestEvent event) { event.getServletContext().setAttribute("found", ByteArrayResourceStreamHandlerProvider.getGetResourceAsStreamFunction() != null); } } } ================================================ FILE: extension/concurro/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-concurro jar Piranha - Extension - Concurro cloud.piranha.core piranha-core-api ${project.version} compile cloud.piranha.extension piranha-extension-scinitializer ${project.version} provided jakarta.enterprise.concurrent jakarta.enterprise.concurrent-api runtime org.glassfish.concurro concurro runtime default file:///tmp/piranha/extension/concurro/ ================================================ FILE: extension/concurro/src/main/java/cloud/piranha/extension/concurro/ConcurroExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.concurro; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import java.lang.System.Logger; import static java.lang.System.Logger.Level.TRACE; /** * The extension that delivers Concurro to Piranha. * * @author Manfred Riem (mriem@manorrock.com) */ public class ConcurroExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger( ConcurroExtension.class.getName()); /** * Constructor. */ public ConcurroExtension() { } /** * Configure the extension. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { LOGGER.log(TRACE, "Configuring Concurro extension"); } } ================================================ FILE: extension/concurro/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module integrates Conccuro into Piranha. See * https://github.com/eclipse-ee4j/concurro for more information. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.concurro { requires cloud.piranha.core.api; requires static cloud.piranha.extension.scinitializer; } ================================================ FILE: extension/coreprofile/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-coreprofile jar Piranha - Extension - Core Profile This module delivers the extensions for the Jakarta Core Profile. cloud.piranha.core piranha-core-api ${project.version} compile cloud.piranha.extension piranha-extension-annotationscan ${project.version} compile cloud.piranha.extension piranha-extension-annotationscan-classfile ${project.version} compile cloud.piranha.extension piranha-extension-handlestypes ${project.version} compile cloud.piranha.extension piranha-extension-herring ${project.version} compile cloud.piranha.extension piranha-extension-policy ${project.version} compile cloud.piranha.extension piranha-extension-security-servlet ${project.version} compile cloud.piranha.extension piranha-extension-jersey ${project.version} compile cloud.piranha.extension piranha-extension-scinitializer ${project.version} compile cloud.piranha.extension piranha-extension-webxml ${project.version} compile cloud.piranha.extension piranha-extension-weld ${project.version} compile cloud.piranha.extension piranha-extension-yasson ${project.version} compile org.glassfish.jaxb jaxb-runtime runtime default file:///tmp/piranha/extension/coreprofile/ ================================================ FILE: extension/coreprofile/src/main/java/cloud/piranha/extension/coreprofile/CoreProfileExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.coreprofile; import cloud.piranha.core.api.WebApplicationExtension; import cloud.piranha.core.api.WebApplicationExtensionContext; import cloud.piranha.extension.annotationscan.AnnotationScanExtension; import cloud.piranha.extension.annotationscan.classfile.ClassfileAnnotationScanExtension; import cloud.piranha.extension.handlestypes.HandlesTypesExtension; import cloud.piranha.extension.herring.HerringExtension; import cloud.piranha.extension.policy.PolicyExtension; import cloud.piranha.extension.scinitializer.ServletContainerInitializerExtension; import cloud.piranha.extension.security.servlet.ServletSecurityExtension; import cloud.piranha.extension.security.servlet.ServletSecurityManagerExtension; import cloud.piranha.extension.webxml.WebXmlExtension; import cloud.piranha.extension.weld.WeldExtension; /** * The extension that delivers the extensions for Jakarta Core Profile. * * @author Manfred Riem (mriem@manorrock.com) */ public class CoreProfileExtension implements WebApplicationExtension { /** * Constructor. */ public CoreProfileExtension() { } @Override public void extend(WebApplicationExtensionContext context) { context.add(PolicyExtension.class); // JavaPolicy context.add(ServletSecurityManagerExtension.class); // SecurityManager context.add(HandlesTypesExtension.class); // HandlesTypes support context.add(HerringExtension.class); // Herring (JNDI) context.add(WebXmlExtension.class); context.add(getAnnotationScanExtensionClass()); context.add(WeldExtension.class); // CDI / Weld context.add(ServletSecurityExtension.class); // Security implementation context.add(ServletContainerInitializerExtension.class); // ServletContainerInitializer } private static Class getAnnotationScanExtensionClass() { if (System.getProperty(ClassfileAnnotationScanExtension.EXPERIMENTAL_PROPERTY) != null) { return ClassfileAnnotationScanExtension.class; // Annotation scanning using the new Classfile API } return AnnotationScanExtension.class; // Annotation scanning } } ================================================ FILE: extension/coreprofile/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the extension for Jakarta Core Profile. * *

* The following extensions and/or dependencies are included: *

*
    *
  • Annotation Scanning
  • *
  • HandlesTypes support
  • *
  • Herring (JNDI)
  • *
  • Jersey (REST)
  • *
  • Parsson (JSON)
  • *
  • ServletContainerInitializer
  • *
  • web.xml support
  • *
  • Weld (CDI)
  • *
  • Yasson (JSON-B)
  • *
  • Servlet security (Basic auth, security constraints)
  • *
*/ module cloud.piranha.extension.coreprofile { exports cloud.piranha.extension.coreprofile; opens cloud.piranha.extension.coreprofile; requires transitive cloud.piranha.core.api; requires cloud.piranha.extension.annotationscan; requires cloud.piranha.extension.annotationscan.classfile; requires cloud.piranha.extension.handlestypes; requires cloud.piranha.extension.herring; requires cloud.piranha.extension.jersey; requires cloud.piranha.extension.policy; requires cloud.piranha.extension.security.servlet; requires cloud.piranha.extension.scinitializer; requires cloud.piranha.extension.webxml; requires cloud.piranha.extension.weld; requires cloud.piranha.extension.yasson; } ================================================ FILE: extension/datasource/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-datasource jar Piranha - Extension - Datasource cloud.piranha.core piranha-core-api ${project.version} com.h2database h2 jakarta.transaction jakarta.transaction-api provided jakarta.enterprise jakarta.enterprise.cdi-api provided org.jacoco jacoco-maven-plugin default-check check BUNDLE INSTRUCTION COVEREDRATIO 0.80 CLASS MISSEDCOUNT 0 default file:///tmp/piranha/extension/datasource/ ================================================ FILE: extension/datasource/src/main/java/cloud/piranha/extension/datasource/DataSourceWrapper.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.datasource; import java.io.PrintWriter; import java.sql.Connection; import java.sql.ConnectionBuilder; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.sql.ShardingKeyBuilder; import java.util.logging.Logger; import javax.sql.DataSource; /** * Wrapper for a data source. * * @author Arjan Tijms * */ public class DataSourceWrapper implements DataSource { /** * The data source wrapped by this wrapper */ private DataSource wrapped; /** * Creates a wrapper with the given data source * * @param wrapped the data source being wrapped */ public DataSourceWrapper(DataSource wrapped) { this.wrapped = wrapped; } /** * Return the data source being wrapped * @return the wrapped data source */ public DataSource getWrapped() { return wrapped; } @Override public T unwrap(Class iface) throws SQLException { return wrapped.unwrap(iface); } @Override public boolean isWrapperFor(Class iface) throws SQLException { return wrapped.isWrapperFor(iface); } @Override public Connection getConnection() throws SQLException { return wrapped.getConnection(); } @Override public Connection getConnection(String username, String password) throws SQLException { return wrapped.getConnection(username, password); } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { return wrapped.getParentLogger(); } @Override public PrintWriter getLogWriter() throws SQLException { return wrapped.getLogWriter(); } @Override public void setLogWriter(PrintWriter out) throws SQLException { wrapped.setLogWriter(out); } @Override public void setLoginTimeout(int seconds) throws SQLException { wrapped.setLoginTimeout(seconds); } @Override public int getLoginTimeout() throws SQLException { return wrapped.getLoginTimeout(); } @Override public ConnectionBuilder createConnectionBuilder() throws SQLException { return wrapped.createConnectionBuilder(); } @Override public ShardingKeyBuilder createShardingKeyBuilder() throws SQLException { return wrapped.createShardingKeyBuilder(); } } ================================================ FILE: extension/datasource/src/main/java/cloud/piranha/extension/datasource/DefaultDatasourceExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.datasource; import static java.lang.System.Logger.Level.DEBUG; import static java.lang.System.Logger.Level.WARNING; import java.lang.System.Logger; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import jakarta.servlet.ServletContainerInitializer; /** * The WebApplicationExtension that adds the StandardTempDirInitializer. * */ public class DefaultDatasourceExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(DefaultDatasourceExtension.class.getName()); /** * Constructor. */ public DefaultDatasourceExtension() { } @Override public void configure(WebApplication webApplication) { LOGGER.log(DEBUG, "Adding the DefaultDatasourceExtension"); try { webApplication.addInitializer( webApplication.getClassLoader() .loadClass(DefaultDatasourceInitializer.class.getName()) .asSubclass(ServletContainerInitializer.class) .getDeclaredConstructor() .newInstance()); } catch (SecurityException | ReflectiveOperationException | IllegalArgumentException ex) { LOGGER.log(WARNING, "Unable to add the DefaultDatasourceExtension", ex); } } } ================================================ FILE: extension/datasource/src/main/java/cloud/piranha/extension/datasource/DefaultDatasourceInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.datasource; import static java.lang.System.Logger.Level.WARNING; import java.lang.System.Logger; import java.util.Set; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; /** * The ServletContainerInitializer that creates the default data source under * the "java:comp/DefaultDataSource" name. */ public class DefaultDatasourceInitializer implements ServletContainerInitializer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(DefaultDatasourceInitializer.class.getName()); /** * Constructor. */ public DefaultDatasourceInitializer() { } @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { createDataSource( "java:comp/DefaultDataSource", "org.h2.jdbcx.JdbcDataSource", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"); } private void createDataSource(String name, String className, String url) { try { DataSource dataSource = (DataSource) Class.forName(className).getDeclaredConstructor().newInstance(); dataSource.getClass().getMethod("setUrl", String.class) .invoke(dataSource, url); new InitialContext().bind(name, new TxJoiningDataSource(dataSource)); } catch (IllegalArgumentException | ReflectiveOperationException | SecurityException | NamingException e) { LOGGER.log(WARNING, "Unable to create DataSource", e); } } } ================================================ FILE: extension/datasource/src/main/java/cloud/piranha/extension/datasource/TxJoiningDataSource.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.datasource; import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import javax.sql.XAConnection; import javax.sql.XADataSource; import jakarta.enterprise.inject.spi.CDI; import jakarta.transaction.RollbackException; import jakarta.transaction.SystemException; import jakarta.transaction.Transaction; import jakarta.transaction.TransactionManager; /** * Data source wrapper that adds connections retrieved from it to any ongoing transaction. * * @author Arjan Tijms * */ public class TxJoiningDataSource extends DataSourceWrapper { /** * Stores the transactionManager */ private transient TransactionManager transactionManager; /** * Creates a wrapper with the given data source * * @param dataSource the data source being wrapped */ public TxJoiningDataSource(DataSource dataSource) { super(dataSource); } @Override public Connection getConnection() throws SQLException { try { Transaction transaction = getTransactionManager().getTransaction(); if (transaction != null) { DataSource dataSource = getWrapped(); if (dataSource instanceof XADataSource xaDataSource) { XAConnection xaConnection = xaDataSource.getXAConnection(); transaction.enlistResource(xaConnection.getXAResource()); return xaConnection.getConnection(); } } return super.getConnection(); } catch (SystemException | IllegalStateException | RollbackException e) { throw new SQLException(e); } } @Override public Connection getConnection(String username, String password) throws SQLException { try { Transaction transaction = getTransactionManager().getTransaction(); if (transaction != null) { DataSource dataSource = getWrapped(); if (dataSource instanceof XADataSource xaDataSource) { XAConnection xaConnection = xaDataSource.getXAConnection(username, password); transaction.enlistResource(xaConnection.getXAResource()); return xaConnection.getConnection(); } } return super.getConnection(username, password); } catch (SystemException | IllegalStateException | RollbackException e) { throw new SQLException(e); } } /** * Attempt to get a non-transactional connection from this data source. * * @return a connection that is not explicitly added to any ongoing transaction. * @throws SQLException when something goes wrong */ public Connection getNonTxConnection() throws SQLException { return super.getConnection(); } /** * Attempt to get a non-transactional connection from this data source. * * @param username name to login to the database * @param password password to use to login to the database * @return a connection that is not explicitly added to any ongoing transaction. * @throws SQLException when something goes wrong */ public Connection getNonTxConnection(String username, String password) throws SQLException { return super.getConnection(username, password); } private TransactionManager getTransactionManager() { if (transactionManager == null) { transactionManager = CDI.current().select(TransactionManager.class).get(); } return transactionManager; } } ================================================ FILE: extension/datasource/src/main/java/cloud/piranha/extension/datasource/XADataSourceWrapper.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.datasource; import java.io.PrintWriter; import java.sql.Connection; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.sql.ShardingKeyBuilder; import java.util.logging.Logger; import javax.sql.DataSource; import javax.sql.XAConnection; import javax.sql.XADataSource; /** * Wrapper for an XADataSource, that implements DataSource as well. * *

* This wrapper can be specifically used in places where a DataSource is required but an XADataSource * is available. * * @author Arjan Tijms * */ public class XADataSourceWrapper implements DataSource, XADataSource { /** * The data source wrapped by this wrapper */ private XADataSource wrapped; /** * Creates a wrapper with the given data source * * @param wrapped the data source being wrapped */ public XADataSourceWrapper(XADataSource wrapped) { this.wrapped = wrapped; } /** * Return the data source being wrapped * @return the wrapped data source */ public XADataSource getWrapped() { return wrapped; } @Override public T unwrap(Class iface) throws SQLException { if (!isWrapperFor(iface)) { throw new SQLException("Can not unwrap for " + iface); } return (T) wrapped; } @Override public boolean isWrapperFor(Class iface) throws SQLException { return iface != null && iface.isAssignableFrom(wrapped.getClass()); } @Override public Connection getConnection() throws SQLException { return getXAConnection().getConnection(); } @Override public Connection getConnection(String username, String password) throws SQLException { return getXAConnection(username, password).getConnection(); } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { return wrapped.getParentLogger(); } @Override public PrintWriter getLogWriter() throws SQLException { return wrapped.getLogWriter(); } @Override public void setLogWriter(PrintWriter out) throws SQLException { wrapped.setLogWriter(out); } @Override public void setLoginTimeout(int seconds) throws SQLException { wrapped.setLoginTimeout(seconds); } @Override public int getLoginTimeout() throws SQLException { return wrapped.getLoginTimeout(); } @Override public ShardingKeyBuilder createShardingKeyBuilder() throws SQLException { return wrapped.createShardingKeyBuilder(); } @Override public XAConnection getXAConnection() throws SQLException { return wrapped.getXAConnection(); } @Override public XAConnection getXAConnection(String user, String password) throws SQLException { return wrapped.getXAConnection(user, password); } } ================================================ FILE: extension/datasource/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the datasource extension * *

* This extension adds a default DataSource under the * java:comp/DefaultDataSource JNDI name as specified by Jakarta * EE. *

* * @author Arjan Tijms */ module cloud.piranha.extension.datasource { exports cloud.piranha.extension.datasource; opens cloud.piranha.extension.datasource; requires transitive cloud.piranha.core.api; requires transitive java.sql; requires java.naming; requires jakarta.cdi; requires jakarta.transaction; } ================================================ FILE: extension/datasource/src/site/markdown/index.md ================================================ # Piranha DataSource Extension The DataSource extension delivers you with a default DataSource in a Piranha runtime. ## Default DataSource Configuration The default DataSource is configured with the following details: - **Name**: `java:comp/DefaultDataSource` - **Class**: `org.h2.jdbcx.JdbcDataSource` - **URL**: `jdbc:h2:mem:test;DB_CLOSE_DELAY=-1` ## Override the default DataSource using web.xml You override the default DataSource using the snippet below: ```xml java:comp/DefaultDataSource my.CustomDataSource jdbc:custom://localhost:3306/mydb myuser mypassword myProperty myValue ``` For runtimes that do not include the Piranha web.xml extension you will need to add it if you want to override the default DataSource as described above. You can do this by adding the following dependency to your pom.xml: ```xml cloud.piranha.extension piranha-extension-webxml ${piranha.version} ``` ## Register Any Other DataSource You can register any other DataSource by using a similar approach. Below is an example of configuring a PostgreSQL DataSource: ```xml java:comp/PostgreSQLDataSource org.postgresql.ds.PGSimpleDataSource jdbc:postgresql://localhost:5432/mydb myuser mypassword ssl true ``` Make sure to replace the values with your actual PostgreSQL database details. ================================================ FILE: extension/datasource/src/site/site.xml ================================================ ================================================ FILE: extension/declared/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-declared jar Piranha - Extension - Declared cloud.piranha.core piranha-core-api ${project.version} provided cloud.piranha.core piranha-core-impl ${project.version} test org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test ================================================ FILE: extension/declared/src/main/java/cloud/piranha/extension/declared/DeclaredExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.declared; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import cloud.piranha.extension.declared.internal.InternalDeclaredInitializer; /** * The Declared extension delivers the InternalDeclaredInitializer. * * @author Manfred Riem (mriem@manorrock.com) */ public class DeclaredExtension implements WebApplicationExtension { /** * Constructor. */ public DeclaredExtension() { } /** * Configure the web application. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { webApplication.addInitializer(new InternalDeclaredInitializer()); } } ================================================ FILE: extension/declared/src/main/java/cloud/piranha/extension/declared/internal/InternalDeclaredInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.declared.internal; import cloud.piranha.core.api.WebApplication; import static cloud.piranha.core.api.WebApplication.Status.DECLARED; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import java.util.Set; /** * The ServletContainerInitializer that signals the end of declared web.xml, * web-fragment.xml files and Servlet annotation support processing. * * @author Manfred Riem (mriem@manorrock.com) */ public class InternalDeclaredInitializer implements ServletContainerInitializer { @Override public void onStartup(Set> classes, ServletContext context) throws ServletException { WebApplication webApplication = (WebApplication) context; webApplication.setStatus(DECLARED); } } ================================================ FILE: extension/declared/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * The module that delivers declared extension. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.declared { exports cloud.piranha.extension.declared; opens cloud.piranha.extension.declared; requires cloud.piranha.core.api; requires java.logging; } ================================================ FILE: extension/declared/src/test/java/cloud/piranha/extension/declared/DeclaredExtensionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.declared; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.impl.DefaultWebApplication; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the DeclaredExtension class. * * @author Manfred Riem (mriem@manorrock.com) */ public class DeclaredExtensionTest { /** * Test configure method. */ @Test public void testConfigure() { WebApplication webApplication = new DefaultWebApplication(); DeclaredExtension extension = new DeclaredExtension(); extension.configure(webApplication); webApplication.initialize(); assertTrue(webApplication.isInitialized()); } } ================================================ FILE: extension/declared/src/test/java/cloud/piranha/extension/handlestypes/internal/InternalDeclaredInitializerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.handlestypes.internal; import cloud.piranha.core.api.WebApplication.Status; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.extension.declared.internal.InternalDeclaredInitializer; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; /** * The JUnit tests for the InternalHandlesTypesInitializer class. * * @author Manfred Riem (mriem@manorrock.com) */ public class InternalDeclaredInitializerTest { /** * Test onStartup method. */ @Test public void testOnStartup() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); InternalDeclaredInitializer initializer = new InternalDeclaredInitializer(); initializer.onStartup(null, webApplication); assertEquals(Status.DECLARED, webApplication.getStatus()); } } ================================================ FILE: extension/eclipselink/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-eclipselink jar Piranha - Extension - EclipseLink cloud.piranha.core piranha-core-impl ${project.version} compile cloud.piranha.extension piranha-extension-datasource ${project.version} compile jakarta.persistence jakarta.persistence-api compile org.eclipse.persistence eclipselink compile org.eclipse.persistence org.eclipse.persistence.core compile org.eclipse.persistence org.eclipse.persistence.jpa compile org.eclipse.persistence org.eclipse.persistence.json compile jakarta.activation jakarta.activation-api provided jakarta.enterprise jakarta.enterprise.cdi-api provided jakarta.transaction jakarta.transaction-api provided cloud.piranha piranha-embedded ${project.version} test org.junit.jupiter junit-jupiter-api test org.junit.platform junit-platform-launcher test ================================================ FILE: extension/eclipselink/src/main/java/cloud/piranha/extension/eclipselink/EclipseLinkCdiExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.eclipselink; import jakarta.enterprise.event.Observes; import jakarta.enterprise.inject.literal.InjectLiteral; import jakarta.enterprise.inject.spi.AnnotatedMember; import jakarta.enterprise.inject.spi.BeanManager; import jakarta.enterprise.inject.spi.BeforeBeanDiscovery; import jakarta.enterprise.inject.spi.Extension; import jakarta.enterprise.inject.spi.ProcessAnnotatedType; import jakarta.inject.Inject; import jakarta.persistence.PersistenceContext; /** * CDI Extension that among others makes PersistenceContext injectable via CDI. * * @author Arjan Tijms * */ public class EclipseLinkCdiExtension implements Extension { /** * Constructor. */ public EclipseLinkCdiExtension() { } /** * Register the required annotated types. * * @param beforeBean the before bean discovery event. * @param beanManager the bean manager. */ public void register(@Observes BeforeBeanDiscovery beforeBean, BeanManager beanManager) { addAnnotatedTypes(beforeBean, beanManager, EntityManagerProducer.class, EntityManagerFactoryCreator.class, TxEntityManagerHolder.class, NonTxEntityManagerHolder.class); } /** * Process the annotated types. * * @param the type. * @param pat the process annotated type event. */ public void processAnnotatedType(@Observes ProcessAnnotatedType pat) { pat.configureAnnotatedType() .filterFields(EclipseLinkCdiExtension::shouldInjectionAnnotationBeAdded) .forEach(e -> e.add(InjectLiteral.INSTANCE)); pat.configureAnnotatedType() .filterMethods(EclipseLinkCdiExtension::shouldInjectionAnnotationBeAdded) .forEach(m -> m.add(InjectLiteral.INSTANCE)); } private static boolean shouldInjectionAnnotationBeAdded(AnnotatedMember field) { return !field.isAnnotationPresent(Inject.class) && field.isAnnotationPresent(PersistenceContext.class); } private static void addAnnotatedTypes(BeforeBeanDiscovery beforeBean, BeanManager beanManager, Class... types) { for (Class type : types) { beforeBean.addAnnotatedType(beanManager.createAnnotatedType(type), "EclipseLinkCdiExtension " + type.getName()); } } } ================================================ FILE: extension/eclipselink/src/main/java/cloud/piranha/extension/eclipselink/EclipseLinkExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.eclipselink; import static java.lang.System.Logger.Level.WARNING; import java.lang.System.Logger; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import jakarta.servlet.ServletContainerInitializer; /** * The extension that will enable EclipseLink integration * * @author Arjan Tijms */ public class EclipseLinkExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(EclipseLinkExtension.class.getName()); /** * Constructor. */ public EclipseLinkExtension() { } /** * Configure the web application. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { try { webApplication.addInitializer( webApplication.getClassLoader() .loadClass(EclipseLinkInitializer.class.getName()) .asSubclass(ServletContainerInitializer.class) .getDeclaredConstructor() .newInstance()); } catch (ReflectiveOperationException | SecurityException ex) { LOGGER.log(WARNING, "Unable to enable the EclipseLink extension", ex); } } } ================================================ FILE: extension/eclipselink/src/main/java/cloud/piranha/extension/eclipselink/EclipseLinkInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.eclipselink; import static java.lang.System.Logger.Level.DEBUG; import static java.lang.System.Logger.Level.INFO; import java.lang.System.Logger; import java.util.Set; import cloud.piranha.core.api.WebApplication; import jakarta.enterprise.inject.spi.CDI; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; /** * The EclipseLink initializer. * * @author Arjan Tijms */ public class EclipseLinkInitializer implements ServletContainerInitializer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(EclipseLinkInitializer.class.getName()); /** * Constructor. */ public EclipseLinkInitializer() { } /** * Initialize EclipseLink. * * @param classes the classes. * @param servletContext the Servlet context. * @throws ServletException when a Servlet error occurs. */ @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { WebApplication application = (WebApplication) servletContext; if (!isCDIEnabled()) { return; } LOGGER.log(INFO, "Initializing EclipseLink"); CDI.current() .select(EntityManagerFactoryCreator.class) .get() .setAnnotationManager(application.getManager().getAnnotationManager()); LOGGER.log(DEBUG, "Initialized EclipseLink"); } private boolean isCDIEnabled() { try { CDI.current(); return true; } catch (IllegalStateException e) { return false; } } } ================================================ FILE: extension/eclipselink/src/main/java/cloud/piranha/extension/eclipselink/EntityManagerFactoryCreator.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.eclipselink; import java.lang.reflect.Method; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.CommonDataSource; import javax.sql.DataSource; import javax.sql.XADataSource; import org.eclipse.persistence.internal.jpa.deployment.JPAInitializer; import org.eclipse.persistence.internal.jpa.deployment.PersistenceUnitProcessor; import org.eclipse.persistence.internal.jpa.deployment.SEPersistenceUnitInfo; import org.eclipse.persistence.jpa.Archive; import org.eclipse.persistence.jpa.PersistenceProvider; import cloud.piranha.core.api.AnnotationManager; import cloud.piranha.extension.datasource.TxJoiningDataSource; import cloud.piranha.extension.datasource.XADataSourceWrapper; import jakarta.enterprise.context.ApplicationScoped; import jakarta.persistence.Converter; import jakarta.persistence.Embeddable; import jakarta.persistence.Entity; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.MappedSuperclass; /** * This bean takes care of creating an EntityManagerFactory using EclipseLink specific APIs. * * @author Arjan Tijms */ @ApplicationScoped public class EntityManagerFactoryCreator { /** * Stores previously created EntityManagerFactories per unit name. */ private Map entityManagerFactories = new ConcurrentHashMap<>(); /** * The AnnotationManager used to lookup entity classes etc */ private AnnotationManager annotationManager; /** * Constructor. */ public EntityManagerFactoryCreator() { } /** * Gets the AnnotationManager used to lookup entity classes etc. * * @return the annotation manager. */ public AnnotationManager getAnnotationManager() { return annotationManager; } /** * Sets the AnnotationManager used to lookup entity classes etc. * * @param annotationManager the annotation manager. */ public void setAnnotationManager(AnnotationManager annotationManager) { this.annotationManager = annotationManager; } /** * Gets the EntityManagerFactory corresponding to the unit name. Created if needed. * @param unitName name of the persistence unit * @return EntityManagerFactory corresponding to the unit name */ public EntityManagerFactory get(String unitName) { return entityManagerFactories.computeIfAbsent(unitName, this::create); } @SuppressWarnings("removal") private EntityManagerFactory create(String unitName) { Map properties = new HashMap<>(); // Use the EclipseLink provider directly, since we're specifically integrating EclipseLink here PersistenceProvider provider = new PersistenceProvider(); JPAInitializer persistenceInitializer = provider.getInitializer(unitName, properties); // Get a persistence unit by name first, and if it can't be found see if there's a default one to obtain. // Note that persistence unit info essentially represents a parsed persistence.xml. SEPersistenceUnitInfo persistenceUnitInfo = persistenceInitializer.findPersistenceUnitInfo(unitName, properties); if (persistenceUnitInfo == null && "".equals(unitName)) { persistenceUnitInfo = getDefaultPersistenceUnit(persistenceInitializer, properties); } if (persistenceUnitInfo == null) { throw new IllegalStateException("No persistence unit found for [" + unitName + "]"); } persistenceUnitInfo.setTransactionType(jakarta.persistence.spi.PersistenceUnitTransactionType.JTA); if (persistenceUnitInfo.getJtaDataSource() != null && persistenceUnitInfo.getJtaDataSource().getClass().getName().equals("org.eclipse.persistence.internal.jpa.jdbc.DataSourceImpl")) { for (Method method : persistenceUnitInfo.getJtaDataSource().getClass().getDeclaredMethods()) { if (method.getName().equals("getName")) { try { String name = (String) method.invoke(persistenceUnitInfo.getJtaDataSource()); persistenceUnitInfo.setJtaDataSource(lookupAsDataSource(name)); break; } catch (ReflectiveOperationException | IllegalArgumentException | NamingException e1) { throw new IllegalStateException(e1); } } } } if (persistenceUnitInfo.getJtaDataSource() == null) { persistenceUnitInfo.setJtaDataSource(getDefaultDataSource()); } else { // Wrap the configured data source with one that joins any ongoing transaction persistenceUnitInfo.setJtaDataSource(new TxJoiningDataSource(persistenceUnitInfo.getJtaDataSource())); } // Also set a non JTA data source, which EclipseLink uses for special read only queries if (persistenceUnitInfo.getJtaDataSource() instanceof TxJoiningDataSource txJoiningDataSource) { persistenceUnitInfo.setNonJtaDataSource(txJoiningDataSource.getWrapped()); } // SEPersistenceUnitInfo defaults to exclude unlisted true. We can't yet distinguish between the user setting // this and the class defaulting to it. For now scanned classes are always added. persistenceUnitInfo.getManagedClassNames().addAll( annotationManager.getAnnotations(Entity.class, Embeddable.class, Converter.class, MappedSuperclass.class) .stream() .map(e -> e.getTargetType().getName()) .toList()); // Use GlassFish JNDI names for getting the transaction manager. Eventually this should // be standardised. properties.put("eclipselink.target-server", "Glassfish"); properties.put("eclipselink.connection-pool.force-internal-pool", "true"); EntityManagerFactory entityManagerFactory = provider.createContainerEntityManagerFactory(persistenceUnitInfo, properties); if (entityManagerFactory == null) { throw new IllegalStateException("Cannot create EntityManagerFactory with unitName " + unitName); } return entityManagerFactory; } private SEPersistenceUnitInfo getDefaultPersistenceUnit(JPAInitializer persistenceInitializer, Map properties) { Set persistenceUnitNames = getPersistenceUnitNames(persistenceInitializer); if (persistenceUnitNames.size() != 1) { return null; } String unitName = persistenceUnitNames.iterator().next(); return persistenceInitializer.findPersistenceUnitInfo(unitName, properties); } private Set getPersistenceUnitNames(JPAInitializer persistenceInitializer) { Set persistenceUnitNames = new HashSet<>(); Set archives = PersistenceUnitProcessor.findPersistenceArchives(persistenceInitializer.getInitializationClassLoader()); for (Archive archive : archives) { Iterator persistenceUnits = PersistenceUnitProcessor.getPersistenceUnits(archive, persistenceInitializer.getInitializationClassLoader()).iterator(); while (persistenceUnits.hasNext()) { SEPersistenceUnitInfo persistenceUnitInfoFromArchive = persistenceUnits.next(); if (persistenceInitializer.isPersistenceProviderSupported(persistenceUnitInfoFromArchive.getPersistenceProviderClassName())) { persistenceUnitNames.add(persistenceUnitInfoFromArchive.getPersistenceUnitName()); } } } return persistenceUnitNames; } private DataSource lookupAsDataSource(String name) throws NamingException { CommonDataSource commonDataSource = InitialContext.doLookup(name); if (commonDataSource instanceof DataSource dataSource) { return dataSource; } if (commonDataSource instanceof XADataSource xaDataSource) { return new XADataSourceWrapper(xaDataSource); } throw new IllegalStateException("Type " + commonDataSource.getClass() + " not supported as data source"); } private DataSource getDefaultDataSource() { try { return InitialContext.doLookup("java:comp/DefaultDataSource"); } catch (NamingException e) { throw new IllegalStateException(e); } } } ================================================ FILE: extension/eclipselink/src/main/java/cloud/piranha/extension/eclipselink/EntityManagerProducer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.eclipselink; import static java.util.stream.Collectors.toMap; import java.util.Arrays; import java.util.Map; import jakarta.enterprise.inject.Produces; import jakarta.enterprise.inject.spi.Annotated; import jakarta.enterprise.inject.spi.AnnotatedParameter; import jakarta.enterprise.inject.spi.InjectionPoint; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import jakarta.persistence.PersistenceProperty; /** * This producer takes care of providing a bean to inject for the PersistenceContext annotation. * * @author Arjan Tijms * */ public class EntityManagerProducer { /** * Constructor. */ public EntityManagerProducer() { } /** * * @param injectionPoint the injectionPoint * @return EntityManager */ @Produces public EntityManager produce(InjectionPoint injectionPoint) { Annotated annotated = injectionPoint.getAnnotated(); if (annotated instanceof AnnotatedParameter annotatedParameter) { annotated = annotatedParameter.getDeclaringCallable(); } PersistenceContext persistenceContext = annotated.getAnnotation(PersistenceContext.class); return new PiranhaEntityManager( persistenceContext.unitName(), persistenceContext.type(), persistenceContext.synchronization(), propertiesToMap(persistenceContext.properties())); } private Map propertiesToMap(PersistenceProperty[] properties) { return Arrays.stream(properties) .collect(toMap( PersistenceProperty::name, PersistenceProperty::value)); } } ================================================ FILE: extension/eclipselink/src/main/java/cloud/piranha/extension/eclipselink/NonTxEntityManagerHolder.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.eclipselink; import java.io.Serializable; import java.util.function.Supplier; import jakarta.enterprise.context.RequestScoped; import jakarta.persistence.EntityManager; /** * Bean to store the entity manager during a request. * *

* This should always hold the non-transactional entity manager, as the * transactional one should be hold in a transaction scoped bean. * * @author Arjan Tijms * */ @RequestScoped public class NonTxEntityManagerHolder implements Serializable { private static final long serialVersionUID = 1L; /** * The entity manager that we store for the duration of a request */ private transient EntityManager entityManager; /** * Constructor. */ public NonTxEntityManagerHolder() { } /** * Gets the entity manager or computes and stores it if not yet available. * * @param entityManagerSupplier the supplier to get the entity manager from * @return the new or previously stored entity manager */ public EntityManager computeIfAbsent(Supplier entityManagerSupplier) { if (entityManager == null) { entityManager = entityManagerSupplier.get(); } return entityManager; } } ================================================ FILE: extension/eclipselink/src/main/java/cloud/piranha/extension/eclipselink/PiranhaEntityManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.eclipselink; import static jakarta.persistence.PersistenceContextType.TRANSACTION; import static jakarta.persistence.SynchronizationType.SYNCHRONIZED; import static java.lang.System.Logger.Level.DEBUG; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import java.lang.System.Logger; import java.util.HashMap; import java.util.Map; import cloud.piranha.extension.eclipselink.wrappers.EntityManagerWrapper; import jakarta.enterprise.inject.spi.CDI; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.LockModeType; import jakarta.persistence.PersistenceContextType; import jakarta.persistence.Query; import jakarta.persistence.StoredProcedureQuery; import jakarta.persistence.SynchronizationType; import jakarta.persistence.TransactionRequiredException; import jakarta.persistence.TypedQuery; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.transaction.RollbackException; import jakarta.transaction.Synchronization; import jakarta.transaction.SystemException; import jakarta.transaction.Transaction; import jakarta.transaction.TransactionManager; /** * Entity Manager wrapper suitable for injection that creates the actual entity manager lazily on demand. * */ public class PiranhaEntityManager extends EntityManagerWrapper implements Serializable { private static final long serialVersionUID = 1L; /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(PiranhaEntityManager.class.getName()); /** * Stores the unit name */ private String unitName = ""; /** * (note extended not yet supported) */ private PersistenceContextType contextType = TRANSACTION; /** * Stores the properties */ private Map properties = new HashMap<>(); /** * (note UNSYNCHRONIZED not yet supported) */ private SynchronizationType synchronizationType = SYNCHRONIZED; /** * Stores the entityManagerFactory */ private transient EntityManagerFactory entityManagerFactory; /** * Stores the transactionManager */ private transient TransactionManager transactionManager; /** * Constructor. * * @param unitName the unit name. * @param contextType the persistence context type. * @param synchronizationType the synchronization type. * @param properties the properties. */ public PiranhaEntityManager(String unitName, PersistenceContextType contextType, SynchronizationType synchronizationType, Map properties) { this.unitName = unitName; this.contextType = contextType; this.synchronizationType = synchronizationType; this.properties = properties; } @Override public EntityManager getWrapped() { EntityManager wrappedEntityManager = null; if (contextType == PersistenceContextType.TRANSACTION) { Transaction transaction = getCurrentTransaction(); if (transaction != null) { wrappedEntityManager = getTxScopedEntityManager(); try { final EntityManager closingEntityManager = wrappedEntityManager; transaction.registerSynchronization(new Synchronization() { @Override public void afterCompletion(int status) { closeTxScopedEntityManager(closingEntityManager); } @Override public void beforeCompletion() { } }); } catch (IllegalStateException | RollbackException | SystemException e) { } } else { wrappedEntityManager = getNonTxScopedEntityManager(); } } return wrappedEntityManager; } // ### Checked methods @Override public void persist(Object entity) { tryCheckTransactionActive(); super.persist(entity); } @Override public T merge(T entity) { tryCheckTransactionActive(); return getWrapped().merge(entity); } @Override public void remove(Object entity) { tryCheckTransactionActive(); getWrapped().remove(entity); } @Override public void flush() { checkTransactionActive(); getWrapped().flush(); } @Override public void refresh(Object entity) { tryCheckTransactionActive(); getWrapped().refresh(entity); } @Override public void refresh(Object entity, Map properties) { tryCheckTransactionActive(); getWrapped().refresh(entity, properties); } @Override public void refresh(Object entity, LockModeType lockMode) { tryCheckTransactionActive(); getWrapped().refresh(entity, lockMode); } @Override public void refresh(Object entity, LockModeType lockMode, Map properties) { tryCheckTransactionActive(); getWrapped().refresh(entity, lockMode, properties); } @Override public LockModeType getLockMode(Object o) { checkTransactionActive(); return getWrapped().getLockMode(o); } @Override public void detach(Object o) { tryCheckTransactionActive(); getWrapped().detach(o); } @Override public void joinTransaction() { checkTransactionActive(); getWrapped().joinTransaction(); } // ### Clear context methods @Override public T find(Class entityClass, Object primaryKey) { EntityManager wrappedEntityManager = getWrapped(); T returnValue = wrappedEntityManager.find(entityClass, primaryKey); clearDetachedEntityManager(wrappedEntityManager); return returnValue; } @Override public T find(Class entityClass, Object primaryKey, Map properties) { EntityManager wrappedEntityManager = getWrapped(); T returnValue = wrappedEntityManager.find(entityClass, primaryKey, properties); clearDetachedEntityManager(wrappedEntityManager); return returnValue; } @Override public T find(Class entityClass, Object primaryKey, LockModeType lockMode) { EntityManager wrappedEntityManager = getWrapped(); T returnValue = wrappedEntityManager.find(entityClass, primaryKey, lockMode); clearDetachedEntityManager(wrappedEntityManager); return returnValue; } @Override public T find(Class entityClass, Object primaryKey, LockModeType lockMode, Map properties) { EntityManager wrappedEntityManager = getWrapped(); T returnValue = wrappedEntityManager.find(entityClass, primaryKey, lockMode, properties); clearDetachedEntityManager(wrappedEntityManager); return returnValue; } // ### Result wrapping methods @Override public Query createQuery(String ejbqlString) { EntityManager wrappedEntityManager = getWrapped(); Query returnValue = wrappedEntityManager.createQuery(ejbqlString); return wrapQueryIfDetached(returnValue, wrappedEntityManager); } @Override public TypedQuery createQuery(String ejbqlString, Class resultClass) { EntityManager wrappedEntityManager = getWrapped(); TypedQuery returnValue = wrappedEntityManager.createQuery(ejbqlString, resultClass); return wrapQueryIfDetached(returnValue, wrappedEntityManager); } @Override public TypedQuery createQuery(CriteriaQuery criteriaQuery) { EntityManager wrappedEntityManager = getWrapped(); TypedQuery returnValue = wrappedEntityManager.createQuery(criteriaQuery); return wrapQueryIfDetached(returnValue, wrappedEntityManager); } @Override public Query createNamedQuery(String name) { EntityManager wrappedEntityManager = getWrapped(); Query returnValue = wrappedEntityManager.createNamedQuery(name); return wrapQueryIfDetached(returnValue, wrappedEntityManager); } @Override public TypedQuery createNamedQuery(String name, Class resultClass) { EntityManager wrappedEntityManager = getWrapped(); TypedQuery returnValue = wrappedEntityManager.createNamedQuery(name, resultClass); return wrapQueryIfDetached(returnValue, wrappedEntityManager); } @Override public Query createNativeQuery(String sqlString) { EntityManager wrappedEntityManager = getWrapped(); Query returnValue = wrappedEntityManager.createNativeQuery(sqlString); return wrapQueryIfDetached(returnValue, wrappedEntityManager); } @Override public Query createNativeQuery(String sqlString, Class resultClass) { EntityManager wrappedEntityManager = getWrapped(); Query returnValue = wrappedEntityManager.createNativeQuery(sqlString, resultClass); return wrapQueryIfDetached(returnValue, wrappedEntityManager); } @Override public Query createNativeQuery(String sqlString, String resultSetMapping) { EntityManager wrappedEntityManager = getWrapped(); Query returnValue = wrappedEntityManager.createNativeQuery(sqlString, resultSetMapping); return wrapQueryIfDetached(returnValue, wrappedEntityManager); } @Override public StoredProcedureQuery createNamedStoredProcedureQuery(String name) { EntityManager wrappedEntityManager = getWrapped(); StoredProcedureQuery returnValue = wrappedEntityManager.createNamedStoredProcedureQuery(name); return wrapQueryIfDetached(returnValue, wrappedEntityManager); } @Override public StoredProcedureQuery createStoredProcedureQuery(String procedureName) { EntityManager wrappedEntityManager = getWrapped(); StoredProcedureQuery returnValue = wrappedEntityManager.createStoredProcedureQuery(procedureName); return wrapQueryIfDetached(returnValue, wrappedEntityManager); } @Override public StoredProcedureQuery createStoredProcedureQuery(String procedureName, Class... resultClasses) { EntityManager wrappedEntityManager = getWrapped(); StoredProcedureQuery returnValue = wrappedEntityManager.createStoredProcedureQuery(procedureName, resultClasses); return wrapQueryIfDetached(returnValue, wrappedEntityManager); } @Override public StoredProcedureQuery createStoredProcedureQuery(String procedureName, String... resultSetMappings) { EntityManager wrappedEntityManager = getWrapped(); StoredProcedureQuery returnValue = wrappedEntityManager.createStoredProcedureQuery(procedureName, resultSetMappings); return wrapQueryIfDetached(returnValue, wrappedEntityManager); } // ### Other methods @Override public void close() { throw new IllegalStateException(); } @Override public boolean isOpen() { return true; } @Override public EntityManagerFactory getEntityManagerFactory() { if (entityManagerFactory == null) { entityManagerFactory = CDI.current() .select(EntityManagerFactoryCreator.class) .get() .get(unitName); } return entityManagerFactory; } @Override public Object getDelegate() { return getWrapped(); } // ### Private methods private void tryCheckTransactionActive() { if (contextType != TRANSACTION) { return; } checkTransactionActive(); } private void checkTransactionActive() { if (getCurrentTransaction() == null) { throw new TransactionRequiredException(); } } private Transaction getCurrentTransaction() { try { return getTransactionManager().getTransaction(); } catch (Exception e) { throw new IllegalStateException("Error getting current transaction", e); } } private EntityManager getTxScopedEntityManager() { return CDI.current() .select(TxEntityManagerHolder.class) .get() .computeIfAbsent(() -> getEntityManagerFactory().createEntityManager(synchronizationType, properties)); } private void closeTxScopedEntityManager(final EntityManager entityManager) { if (entityManager != null && entityManager.isOpen()) { try { entityManager.close(); } catch (Throwable th) { LOGGER.log(DEBUG, th); } } } private EntityManager getNonTxScopedEntityManager() { return CDI.current() .select(NonTxEntityManagerHolder.class) .get() .computeIfAbsent(() -> getEntityManagerFactory().createEntityManager(synchronizationType, properties)); } /** * Deserialize the object. * * @param stream the stream. * @throws IOException when an IOException occurs. * @throws ClassNotFoundException when a ClassNotFoundException occurs. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); } private TransactionManager getTransactionManager() { if (transactionManager == null) { transactionManager = CDI.current().select(TransactionManager.class).get(); } return transactionManager; } private boolean isDetached() { return getCurrentTransaction() == null && contextType != PersistenceContextType.EXTENDED; } private StoredProcedureQuery wrapQueryIfDetached(StoredProcedureQuery query, EntityManager delegate) { return isDetached() ? query : query; } private TypedQuery wrapQueryIfDetached(TypedQuery query, EntityManager delegate) { return isDetached() ? query : query; } private Query wrapQueryIfDetached(Query query, EntityManager delegate) { return isDetached() ? query : query; } private void clearDetachedEntityManager(EntityManager entityManager) { if (isDetached()) { entityManager.clear(); } } } ================================================ FILE: extension/eclipselink/src/main/java/cloud/piranha/extension/eclipselink/TxEntityManagerHolder.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.eclipselink; import java.io.Serializable; import java.util.function.Supplier; import jakarta.persistence.EntityManager; import jakarta.transaction.TransactionScoped; /** * Bean to store the entity manager during a transaction. * *

* This should always hold the transactional entity manager, as the * non-transactional one should be hold in a request scoped bean. * * @author Arjan Tijms * */ @TransactionScoped public class TxEntityManagerHolder implements Serializable { private static final long serialVersionUID = 1L; /** * The entity manager that we store for the duration of a transaction */ private EntityManager entityManager; /** * Constructor. */ public TxEntityManagerHolder() { } /** * Gets the entity manager or computes and stores it if not yet available. * * @param entityManagerSupplier the supplier to get the entity manager from * @return the new or previously stored entity manager */ public EntityManager computeIfAbsent(Supplier entityManagerSupplier) { if (entityManager == null) { entityManager = entityManagerSupplier.get(); } return entityManager; } } ================================================ FILE: extension/eclipselink/src/main/java/cloud/piranha/extension/eclipselink/wrappers/EntityManagerWrapper.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.eclipselink.wrappers; import jakarta.persistence.CacheRetrieveMode; import jakarta.persistence.CacheStoreMode; import jakarta.persistence.ConnectionConsumer; import jakarta.persistence.ConnectionFunction; import java.util.List; import java.util.Map; import jakarta.persistence.EntityGraph; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.EntityTransaction; import jakarta.persistence.FindOption; import jakarta.persistence.FlushModeType; import jakarta.persistence.LockModeType; import jakarta.persistence.LockOption; import jakarta.persistence.Query; import jakarta.persistence.RefreshOption; import jakarta.persistence.StoredProcedureQuery; import jakarta.persistence.TypedQuery; import jakarta.persistence.TypedQueryReference; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaSelect; import jakarta.persistence.criteria.CriteriaUpdate; import jakarta.persistence.metamodel.Metamodel; /** * Provides a convenient implementation of the EntityManager interface that can * be subclassed by developers wishing to adapt the entity manager. * *

* This class implements the Wrapper or Decorator pattern. Methods default to * calling through to the wrapped request object. * */ public class EntityManagerWrapper implements EntityManager { /** * The entity manager that we wrap and delegate to */ private EntityManager wrappedEntityManager; /** */ public EntityManagerWrapper() { } /** * * @param entityManager The entity manager to be wrapped */ public EntityManagerWrapper(EntityManager entityManager) { this.wrappedEntityManager = entityManager; } /** * The underlying wrapped EntityManager * * @return underlying wrapped EntityManager */ public EntityManager getWrapped() { return wrappedEntityManager; } @Override public void persist(Object entity) { getWrapped().persist(entity); } @Override public T merge(T entity) { return getWrapped().merge(entity); } @Override public void remove(Object entity) { getWrapped().remove(entity); } @Override public T find(Class entityClass, Object primaryKey) { return getWrapped().find(entityClass, primaryKey); } @Override public T find(Class entityClass, Object primaryKey, Map properties) { return getWrapped().find(entityClass, primaryKey, properties); } @Override public T find(Class entityClass, Object primaryKey, LockModeType lockMode) { return getWrapped().find(entityClass, primaryKey, lockMode); } @Override public T find(Class entityClass, Object primaryKey, LockModeType lockMode, Map properties) { return getWrapped().find(entityClass, primaryKey, lockMode, properties); } @Override public T find(Class type, Object o, FindOption... fos) { return getWrapped().find(type, o, fos); } @Override public T find(EntityGraph eg, Object o, FindOption... fos) { return getWrapped().find(eg, o, fos); } @Override public void flush() { getWrapped().flush(); } @Override public Query createQuery(CriteriaUpdate updateQuery) { return getWrapped().createQuery(updateQuery); } @Override public Query createQuery(CriteriaDelete deleteQuery) { return getWrapped().createQuery(deleteQuery); } @Override public Query createQuery(String ejbqlString) { return getWrapped().createQuery(ejbqlString); } @Override public TypedQuery createQuery(String ejbqlString, Class resultClass) { return getWrapped().createQuery(ejbqlString, resultClass); } @Override public TypedQuery createQuery(CriteriaQuery criteriaQuery) { return getWrapped().createQuery(criteriaQuery); } @Override public TypedQuery createQuery(CriteriaSelect cs) { return getWrapped().createQuery(cs); } @Override public TypedQuery createQuery(TypedQueryReference tqr) { return getWrapped().createQuery(tqr); } @Override public Query createNamedQuery(String name) { return getWrapped().createNamedQuery(name); } @Override public TypedQuery createNamedQuery(String name, Class resultClass) { return getWrapped().createNamedQuery(name, resultClass); } @Override public Query createNativeQuery(String sqlString) { return getWrapped().createNativeQuery(sqlString); } @Override public Query createNativeQuery(String sqlString, Class resultClass) { return getWrapped().createNativeQuery(sqlString, resultClass); } @Override public Query createNativeQuery(String sqlString, String resultSetMapping) { return getWrapped().createNativeQuery(sqlString, resultSetMapping); } @Override public void refresh(Object entity) { getWrapped().refresh(entity); } @Override public void refresh(Object entity, Map properties) { getWrapped().refresh(entity, properties); } @Override public void refresh(Object entity, LockModeType lockMode) { getWrapped().refresh(entity, lockMode); } @Override public void refresh(Object entity, LockModeType lockMode, Map properties) { getWrapped().refresh(entity, lockMode, properties); } @Override public void refresh(Object o, RefreshOption... ros) { getWrapped().refresh(o, ros); } @Override public LockModeType getLockMode(Object o) { return getWrapped().getLockMode(o); } @Override public void detach(Object o) { getWrapped().detach(o); } @Override public void close() { getWrapped().close(); } @Override public boolean isOpen() { return getWrapped().isOpen(); } @Override public EntityManagerFactory getEntityManagerFactory() { return getWrapped().getEntityManagerFactory(); } @Override public void joinTransaction() { getWrapped().joinTransaction(); } @Override public StoredProcedureQuery createNamedStoredProcedureQuery(String name) { return getWrapped().createNamedStoredProcedureQuery(name); } @Override public StoredProcedureQuery createStoredProcedureQuery(String procedureName) { return getWrapped().createStoredProcedureQuery(procedureName); } @Override public StoredProcedureQuery createStoredProcedureQuery(String procedureName, Class... resultClasses) { return getWrapped().createStoredProcedureQuery(procedureName, resultClasses); } @Override public StoredProcedureQuery createStoredProcedureQuery(String procedureName, String... resultSetMappings) { return getWrapped().createStoredProcedureQuery(procedureName, resultSetMappings); } @Override public T getReference(Class entityClass, Object primaryKey) { return getWrapped().getReference(entityClass, primaryKey); } @Override public T getReference(T t) { return getWrapped().getReference(t); } @Override public void setProperty(String propertyName, Object value) { getWrapped().setProperty(propertyName, value); } @Override public Map getProperties() { return getWrapped().getProperties(); } @Override public EntityTransaction getTransaction() { return getWrapped().getTransaction(); } @Override public CriteriaBuilder getCriteriaBuilder() { return getWrapped().getCriteriaBuilder(); } @Override public boolean contains(Object entity) { return getWrapped().contains(entity); } @Override public Metamodel getMetamodel() { return getWrapped().getMetamodel(); } @Override public void lock(Object entity, LockModeType lockMode) { getWrapped().lock(entity, lockMode); } @Override public void lock(Object entity, LockModeType lockMode, Map properties) { getWrapped().lock(entity, lockMode, properties); } @Override public void lock(Object o, LockModeType lmt, LockOption... los) { getWrapped().lock(o, lmt, los); } @Override public void clear() { getWrapped().clear(); } @Override public Object getDelegate() { return getWrapped().getDelegate(); } @Override public FlushModeType getFlushMode() { return getWrapped().getFlushMode(); } @Override public void setFlushMode(FlushModeType flushMode) { getWrapped().setFlushMode(flushMode); } @Override public T unwrap(Class tClass) { return getWrapped().unwrap(tClass); } @Override public boolean isJoinedToTransaction() { return getWrapped().isJoinedToTransaction(); } @Override public EntityGraph createEntityGraph(Class rootType) { return getWrapped().createEntityGraph(rootType); } @Override public EntityGraph createEntityGraph(String graphName) { return getWrapped().createEntityGraph(graphName); } @Override public EntityGraph getEntityGraph(String graphName) { return getWrapped().getEntityGraph(graphName); } @Override public List> getEntityGraphs(Class entityClass) { return getWrapped().getEntityGraphs(entityClass); } @Override public void setCacheRetrieveMode(CacheRetrieveMode crm) { getWrapped().setCacheRetrieveMode(crm); } @Override public void setCacheStoreMode(CacheStoreMode csm) { getWrapped().setCacheStoreMode(csm); } @Override public CacheRetrieveMode getCacheRetrieveMode() { return getWrapped().getCacheRetrieveMode(); } @Override public CacheStoreMode getCacheStoreMode() { return getWrapped().getCacheStoreMode(); } @Override public void runWithConnection(ConnectionConsumer cc) { getWrapped().runWithConnection(cc); } @Override public T callWithConnection(ConnectionFunction cf) { return getWrapped().callWithConnection(cf); } } ================================================ FILE: extension/eclipselink/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ import cloud.piranha.extension.eclipselink.EclipseLinkCdiExtension; import jakarta.enterprise.inject.spi.Extension; /** * This module delivers the EclipseLink integration extension. * *

* This extension integrates EclipseLink into Piranha. See * https://github.com/eclipse-ee4j/eclipselink for more information about its * project. *

* * @author Arjan Tijms */ module cloud.piranha.extension.eclipselink { exports cloud.piranha.extension.eclipselink; opens cloud.piranha.extension.eclipselink; provides Extension with EclipseLinkCdiExtension; requires transitive cloud.piranha.core.api; requires cloud.piranha.core.impl; requires cloud.piranha.extension.datasource; requires eclipselink; requires jakarta.cdi; requires transitive jakarta.servlet; requires jakarta.transaction; requires java.naming; } ================================================ FILE: extension/eclipselink/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension ================================================ cloud.piranha.extension.eclipselink.EclipseLinkCdiExtension ================================================ FILE: extension/epicyro/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-epicyro jar Piranha - Extension - Epicyro org.glassfish.epicyro epicyro compile jakarta.servlet jakarta.servlet-api cloud.piranha.core piranha-core-impl ${project.version} compile jakarta.authentication jakarta.authentication-api provided ================================================ FILE: extension/epicyro/src/main/java/cloud/piranha/extension/epicyro/AuthenticationFilter.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.epicyro; import static cloud.piranha.core.api.SecurityManager.AuthenticateSource.PRE_REQUEST_CONTAINER; import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN; import java.io.IOException; import jakarta.servlet.FilterChain; import jakarta.servlet.FilterConfig; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.http.HttpFilter; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import cloud.piranha.core.api.FilterPriority; import cloud.piranha.core.api.SecurityManager; import cloud.piranha.core.api.WebApplication; /** * This filter is uses to call a Jakarta Authentication system module at the start of an HTTP request. * *

* Note, this Filter *MUST* be installed after the authorization pre-filter and before the authorization filter. * * @author Arjan Tijms * */ public class AuthenticationFilter extends HttpFilter implements FilterPriority { private static final long serialVersionUID = 1L; /** * Stores the priority. */ private static int priority = 5; /** * Stores the security manager. */ private transient SecurityManager securityManager; /** * Constructor. */ public AuthenticationFilter() { } @Override public int getPriority() { return priority; } @Override public void init(FilterConfig filterConfig) throws ServletException { securityManager = ((WebApplication) filterConfig.getServletContext()) .getManager().getSecurityManager(); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain); } @Override protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { if (securityManager.authenticate(request, response, PRE_REQUEST_CONTAINER)) { chain.doFilter( securityManager.getAuthenticatedRequest(request, response), securityManager.getAuthenticatedResponse(request, response)); securityManager.postRequestProcess(request, response); return; } if ((response.getStatus() < 400 || response.getStatus() > 599) && !response.isCommitted()) { // Authentication Mechanism did not set an error status. Set the default 403 here. response.setStatus(SC_FORBIDDEN); response.getWriter().println("Forbidden"); } } } ================================================ FILE: extension/epicyro/src/main/java/cloud/piranha/extension/epicyro/AuthenticationInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.epicyro; import cloud.piranha.core.api.AuthenticatedIdentity; import cloud.piranha.core.api.SecurityManager; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.impl.DefaultAuthenticatedIdentity; import static jakarta.security.auth.message.config.AuthConfigFactory.DEFAULT_FACTORY_SECURITY_PROPERTY; import jakarta.servlet.FilterRegistration; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import java.io.IOException; import java.io.InputStream; import java.lang.System.Logger; import static java.lang.System.Logger.Level.DEBUG; import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.file.Files.exists; import static java.nio.file.Files.readString; import java.nio.file.Path; import java.nio.file.Paths; import java.security.Security; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.glassfish.epicyro.config.factory.ConfigParser; import org.glassfish.epicyro.config.factory.DefaultConfigFactory; import org.glassfish.epicyro.config.factory.DefaultConfigParser; import org.glassfish.epicyro.config.helper.Caller; import org.glassfish.epicyro.services.DefaultAuthenticationService; import org.glassfish.epicyro.services.InMemoryStore; /** * The Epicyro initializer. * * @author Arjan Tijms * @author Manfred Riem (mriem@manorrock.com) */ public class AuthenticationInitializer implements ServletContainerInitializer { /** * Stores the auth module class name. */ public static final String AUTH_MODULE_CLASS = AuthenticationInitializer.class.getName() + ".auth.module.class"; /** * Stores the auth service name. */ public static final String AUTH_SERVICE = AuthenticationInitializer.class.getName() + ".auth.service"; /** * Stores the logger. */ public static final Logger LOGGER = System.getLogger(AuthenticationInitializer.class.getName()); /** * Constructor. */ public AuthenticationInitializer() { } /** * Initialize Epicyro. * * @param classes the classes. * @param servletContext the Servlet context. * @throws ServletException when a Servlet error occurs. */ @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { LOGGER.log(DEBUG, "Initializing Epicyro"); String appContextId = servletContext.getVirtualServerName() + " " + servletContext.getContextPath(); // This sets the authentication factory to the default factory. This factory stores and retrieves // the authentication artifacts. Security.setProperty(DEFAULT_FACTORY_SECURITY_PROPERTY, DefaultConfigFactory.class.getName()); Map options = new HashMap<>(); // Gets the authentication module class that was configured externally Class authModuleClass = getAuthModuleClass(servletContext, options); // Defines the modules that we have available. Here it's only a single fixed module. ConfigParser configParser = new DefaultConfigParser(authModuleClass); // Indicates the module we want to use options.put("authModuleId", authModuleClass.getSimpleName()); // This authentication service installs an authentication config provider in the default factory, which // is the one we setup above. This authentication config provider uses the passed-in configParser to // retrieve configuration for authentication modules from. DefaultAuthenticationService authenticationService = new DefaultAuthenticationService(appContextId, options, configParser, null); servletContext.setAttribute(AUTH_SERVICE, authenticationService); initIdentityStore(servletContext); setUsernamePasswordLoginHandler(servletContext, authenticationService); addFilter(servletContext, AuthenticationFilter.class); } private void setUsernamePasswordLoginHandler(ServletContext servletContext, DefaultAuthenticationService authenticationService) { WebApplication webApplication = (WebApplication) servletContext; webApplication.getManager().getSecurityManager().setUsernamePasswordLoginHandler( (request, username, password) -> callerToIdentity(authenticationService.login(username, password)) ); } private AuthenticatedIdentity callerToIdentity(Caller caller) { if (caller == null) { return null; } return new DefaultAuthenticatedIdentity(caller.getCallerPrincipal(), caller.getGroups()); } private Class getAuthModuleClass(ServletContext servletContext, Map options) { Class authModuleClass = (Class) servletContext.getAttribute(AUTH_MODULE_CLASS); if (authModuleClass == null) { authModuleClass = DoNothingServerAuthModule.class; WebApplication webApplication = (WebApplication) servletContext; SecurityManager securityManager = webApplication.getManager().getSecurityManager(); if (securityManager != null && securityManager.getAuthMethod() != null) { options.put("authMethod", securityManager.getAuthMethod()); options.put("formErrorPage", securityManager.getFormErrorPage()); options.put("formLoginPage", securityManager.getFormLoginPage()); options.put("realmName", securityManager.getRealmName()); } } return authModuleClass; } void initIdentityStore(ServletContext servletContext) throws ServletException { try { // Try system property first, it overrides all String callers = System.getProperty("io.piranha.identitystore.callers"); if (callers == null) { // Try the web archive next, it overrides global InputStream xmlStream = servletContext.getResourceAsStream("WEB-INF/piranha-callers.xml"); if (xmlStream != null) { callers = new String(xmlStream.readAllBytes(), UTF_8); } // Try global (inside the installed server) last if (callers == null) { Path callerPath = Paths.get("etc/piranha-callers.xml"); if (exists(callerPath)) { callers = readString(callerPath); } } } InMemoryStore.initFromString(callers); } catch (IOException e) { throw new ServletException(e); } } private void addFilter(ServletContext servletContext, Class filterClass) { @SuppressWarnings({"unchecked", "rawtypes"}) FilterRegistration.Dynamic dynamic = servletContext.addFilter(filterClass.getSimpleName(), (Class) filterClass); dynamic.setAsyncSupported(true); ((WebApplication) servletContext).addFilterMapping(filterClass.getSimpleName(), "/*"); } } ================================================ FILE: extension/epicyro/src/main/java/cloud/piranha/extension/epicyro/DoNothingServerAuthModule.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.epicyro; import static jakarta.security.auth.message.AuthStatus.SEND_SUCCESS; import static jakarta.security.auth.message.AuthStatus.SUCCESS; import java.io.IOException; import java.security.Principal; import java.util.Map; import javax.security.auth.Subject; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import jakarta.security.auth.message.AuthException; import jakarta.security.auth.message.AuthStatus; import jakarta.security.auth.message.MessageInfo; import jakarta.security.auth.message.MessagePolicy; import jakarta.security.auth.message.callback.CallerPrincipalCallback; import jakarta.security.auth.message.module.ServerAuthModule; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * Default SAM that does nothing * * @author Arjan Tijms */ public class DoNothingServerAuthModule implements ServerAuthModule { /** * Stores the handler. */ private CallbackHandler handler; /** * Stores the supported message types. */ private Class[] supportedMessageTypes = new Class[]{HttpServletRequest.class, HttpServletResponse.class}; /** * Constructor. */ public DoNothingServerAuthModule() { } @Override public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler handler, @SuppressWarnings("rawtypes") Map options) throws AuthException { this.handler = handler; } @Override public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { try { // The JASPIC protocol for "do nothing" handler.handle(new Callback[]{new CallerPrincipalCallback(clientSubject, (Principal) null)}); return SUCCESS; } catch (IOException | UnsupportedCallbackException e) { throw (AuthException) new AuthException().initCause(e); } } @Override public Class[] getSupportedMessageTypes() { return supportedMessageTypes; } @Override public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException { return SEND_SUCCESS; } @Override public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { // as we do nothing, nothing is to be done here. } } ================================================ FILE: extension/epicyro/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module integrates Epicyro into Piranha. See * https://github.com/eclipse-ee4j/epicyro for more information. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.epicyro { exports cloud.piranha.extension.epicyro; opens cloud.piranha.extension.epicyro; requires cloud.piranha.core.api; requires cloud.piranha.core.impl; requires jakarta.security.auth.message; requires transitive org.glassfish.epicyro; } ================================================ FILE: extension/epicyro/src/test/java/cloud/piranha/extension/epicyro/AuthenticationInitializerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.epicyro; import cloud.piranha.core.impl.DefaultWebApplication; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** * The JUnit test for the AuthenticationInitializer class. * * @author Manfred Riem (mriem@manorrock.com) */ public class AuthenticationInitializerTest { /** * Test onStartup method. */ @Disabled @Test public void testOnStartup() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); AuthenticationInitializer initializer = new AuthenticationInitializer(); initializer.onStartup(null, webApplication); } } ================================================ FILE: extension/exousia/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-exousia jar Piranha - Extension - Exousia cloud.piranha.core piranha-core-impl ${project.version} compile jakarta.authorization jakarta.authorization-api compile jakarta.servlet jakarta.servlet-api org.glassfish.exousia exousia compile jakarta.servlet jakarta.servlet-api org.javassist javassist 3.30.2-GA ================================================ FILE: extension/exousia/src/main/java/cloud/piranha/extension/exousia/AuthorizationFilter.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.exousia; import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN; import java.io.IOException; import jakarta.servlet.FilterChain; import jakarta.servlet.FilterConfig; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.http.HttpFilter; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import cloud.piranha.core.api.FilterPriority; import cloud.piranha.core.api.SecurityManager; import cloud.piranha.core.api.WebApplication; /** * This filter is used to call a Jakarta Authorization system module at the * start of an HTTP request. * *

* Note, this Filter should be installed after the AuthorizationPre filter, and * after the AuthenticationFilter, but before any application filters. * * @author Arjan Tijms * */ public class AuthorizationFilter extends HttpFilter implements FilterPriority { /** * Stores the priority. */ private static final int PRIORITY = 10; private static final long serialVersionUID = 1178463438252262094L; /** * Stores the security manager. * */ private transient SecurityManager securityManager; /** * Constructor. */ public AuthorizationFilter() { } @Override public int getPriority() { return PRIORITY; } @Override public void init(FilterConfig filterConfig) throws ServletException { securityManager = ((WebApplication) filterConfig.getServletContext()) .getManager().getSecurityManager(); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain); } @Override protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { if (!securityManager.isCallerAuthorizedForResource(request)) { response.setStatus(SC_FORBIDDEN); return; } chain.doFilter(request, response); } } ================================================ FILE: extension/exousia/src/main/java/cloud/piranha/extension/exousia/AuthorizationInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.exousia; import java.util.Set; import jakarta.servlet.FilterRegistration; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import cloud.piranha.core.api.WebApplication; /** * The Exousia initializer. * * @author Arjan Tijms */ public class AuthorizationInitializer implements ServletContainerInitializer { /** * Constructor. */ public AuthorizationInitializer() { } /** * Installs the authorization filter * * @param classes the classes. * @param servletContext the Servlet context. * @throws ServletException when a Servlet error occurs. */ @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { WebApplication context = (WebApplication) servletContext; FilterRegistration.Dynamic dynamic = servletContext.addFilter(AuthorizationFilter.class.getSimpleName(), AuthorizationFilter.class); dynamic.setAsyncSupported(true); context.addFilterMapping(AuthorizationFilter.class.getSimpleName(), "/*"); } } ================================================ FILE: extension/exousia/src/main/java/cloud/piranha/extension/exousia/AuthorizationPostInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.exousia; import static cloud.piranha.extension.exousia.AuthorizationPreInitializer.CONSTRAINTS; import static cloud.piranha.extension.exousia.AuthorizationPreInitializer.PERROLE_PERMISSIONS; import static cloud.piranha.extension.exousia.AuthorizationPreInitializer.SECURITY_ANNOTATIONS; import static cloud.piranha.extension.exousia.AuthorizationPreInitializer.SECURITY_ELEMENTS; import static cloud.piranha.extension.exousia.AuthorizationPreInitializer.UNCHECKED_PERMISSIONS; import static java.util.Collections.disjoint; import static java.util.Collections.emptyList; import static java.util.stream.Collectors.toSet; import static org.glassfish.exousia.constraints.SecurityConstraint.join; import java.security.Permission; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.glassfish.exousia.AuthorizationService; import org.glassfish.exousia.constraints.SecurityConstraint; import org.glassfish.exousia.constraints.WebResourceCollection; import org.glassfish.exousia.mapping.SecurityRoleRef; import cloud.piranha.core.api.WebApplication; import jakarta.security.jacc.PolicyConfiguration; import jakarta.security.jacc.PolicyContextException; import jakarta.servlet.FilterRegistration; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; /** * The Exousia initializer. * * @author Arjan Tijms */ public class AuthorizationPostInitializer implements ServletContainerInitializer { /** * Stores the Piranha to Exousia converter. */ PiranhaToExousiaConverter piranhaToExousiaConverter = new PiranhaToExousiaConverter(); /** * Constructor. */ public AuthorizationPostInitializer() { } /** * Initialize Exousia * * @param classes the classes. * @param servletContext the Servlet context. * @throws ServletException when a Servlet error occurs. */ @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { WebApplication context = (WebApplication) servletContext; // Get the main (Exousia) authorization service, which implements the various entry points (an SPI) // for a runtime to make use of Jakarta Authorization AuthorizationService authorizationService = getAuthorizationService(context); // Join together in one list the constraints set by the servlet security elements, and the // piranha specific security constraint List securityConstraints = getAllScurityConstraints(context); for (SecurityConstraint securityConstraint : securityConstraints) { context.getManager().getSecurityManager().declareRoles(securityConstraint.getRolesAllowed()); } if (hasPermissionsSet(context)) { setPermissions(context, authorizationService); } else { setConstraints(context, authorizationService, securityConstraints); } authorizationService.commitPolicy(); addAuthorizationPreFilter(context); } // ### Private methods private AuthorizationService getAuthorizationService(WebApplication context) throws ServletException { return getAttribute(context, AuthorizationPreInitializer.AUTHZ_SERVICE); } private List getAllScurityConstraints(WebApplication context) { List webXmlConstraints = getConstraintsFromSecurityManager(context); List annotationConstraints = filterAnnotatedConstraints( webXmlConstraints, getConstraintsFromSecurityAnnotations(context)); List allScurityConstraints = join( getConstraintsFromSecurityElements(context), annotationConstraints, getOptionalAttribute(context, CONSTRAINTS), webXmlConstraints); if (allScurityConstraints == null) { return emptyList(); } return allScurityConstraints; } private void addAuthorizationPreFilter(WebApplication context) { FilterRegistration.Dynamic dynamic = context.addFilter(AuthorizationPreFilter.class.getSimpleName(), AuthorizationPreFilter.class); dynamic.setAsyncSupported(true); context.addFilterMapping(AuthorizationPreFilter.class.getSimpleName(), "/*"); } private List filterAnnotatedConstraints(List webXmlConstraints, List annotationConstraints) { if (isAnyNull(webXmlConstraints, annotationConstraints)) { return annotationConstraints; } // Servlet Spec 13.4.1 // // When a security-constraint in the portable deployment descriptor includes a url-pattern // that is an exact match for a pattern mapped to a class annotated with @ServletSecurity, // the annotation must have no effect on the constraints enforced by the Servlet container // on the pattern. // Index all URL patterns in web.xml Set webXmlUrlPatterns = webXmlConstraints .stream() .flatMap(e -> e.getWebResourceCollections().stream()) .flatMap(e -> e.getUrlPatterns().stream()) .collect(toSet()) ; List filteredAnnotationConstraints = new ArrayList<>(); for (SecurityConstraint annotationConstraint : annotationConstraints) { List webResourceCollections = new ArrayList<>(); for (WebResourceCollection webResourceCollection : annotationConstraint.getWebResourceCollections()) { WebResourceCollection newWebResourceCollection = webResourceCollection; if (!disjoint(webXmlUrlPatterns, webResourceCollection.getUrlPatterns())) { Set complementPatterns = new HashSet<>(webResourceCollection.getUrlPatterns()); complementPatterns.removeAll(webXmlUrlPatterns); if (complementPatterns.isEmpty()) { newWebResourceCollection = null; } else { newWebResourceCollection = new WebResourceCollection( complementPatterns, webResourceCollection.getHttpMethods(), webResourceCollection.getHttpMethodOmissions()); } } if (newWebResourceCollection != null) { webResourceCollections.add(newWebResourceCollection); } } if (!webResourceCollections.isEmpty()) { filteredAnnotationConstraints.add(new SecurityConstraint( webResourceCollections, annotationConstraint.getRolesAllowed(), annotationConstraint.getTransportGuarantee())); } } return filteredAnnotationConstraints; } /** * Get the security constraints from security elements. * * @param servletContext the Servlet context. * @return the list of security constraints. */ private List getConstraintsFromSecurityElements(ServletContext servletContext) { return piranhaToExousiaConverter.getConstraintsFromSecurityElements(getOptionalAttribute(servletContext, SECURITY_ELEMENTS)); } /** * Get the security constraints from annotations. * * @param servletContext the Servlet context. * @return the list of security constraints. */ private List getConstraintsFromSecurityAnnotations(ServletContext servletContext) { return piranhaToExousiaConverter.getConstraintsFromSecurityAnnotations(getOptionalAttribute(servletContext, SECURITY_ANNOTATIONS)); } /** * Get security constraints from web.xml. * * @param webApplication the web application. * @return the list of security constraints. */ private List getConstraintsFromSecurityManager(WebApplication webApplication) { return piranhaToExousiaConverter.getConstraintsFromSecurityManager( webApplication.getManager().getSecurityManager()); } /** * Get the security role refs from web.xml. * * @param webApplication the web application. * @return the map of security role refs. * @throws ServletException when a Servlet error occurs. */ public Map> getSecurityRoleRefsFromWebXml(WebApplication webApplication) throws ServletException { return piranhaToExousiaConverter.getSecurityRoleRefsFromSecurityManager(webApplication.getServletRegistrations().keySet(), webApplication); } private boolean hasPermissionsSet(ServletContext servletContext) { return getOptionalAttribute(servletContext, UNCHECKED_PERMISSIONS) != null || getOptionalAttribute(servletContext, PERROLE_PERMISSIONS) != null; } private void setPermissions(ServletContext servletContext, AuthorizationService authorizationService) { // Add permissions to the policy configuration, which is the repository that the policy (authorization module) // uses PolicyConfiguration policyConfiguration = authorizationService.getPolicyConfiguration(); try { List unchecked = getOptionalAttribute(servletContext, UNCHECKED_PERMISSIONS); if (unchecked != null) { for (Permission permission : unchecked) { policyConfiguration.addToUncheckedPolicy(permission); } } List> perRole = getOptionalAttribute(servletContext, PERROLE_PERMISSIONS); if (perRole != null) { for (Entry perRoleEntry : perRole) { policyConfiguration.addToRole(perRoleEntry.getKey(), perRoleEntry.getValue()); } } } catch (PolicyContextException e) { throw new IllegalStateException(e); } } private void setConstraints(WebApplication context, AuthorizationService authorizationService, List securityConstraints) throws ServletException { authorizationService.addConstraintsToPolicy( securityConstraints, context.getManager().getSecurityManager().getRoles(), context.getManager().getSecurityManager().getDenyUncoveredHttpMethods(), getSecurityRoleRefsFromWebXml(context)); } // ### Utility methods private static T getAttribute(ServletContext servletContext, String name) throws ServletException { T t = getOptionalAttribute(servletContext, name); if (t == null) { throw new ServletException("Attribute " + name + " not specified"); } return t; } private static T getOptionalAttribute(ServletContext servletContext, String name) { @SuppressWarnings("unchecked") T t = (T) servletContext.getAttribute(name); return t; } private static boolean isAnyNull(Object... values) { for (Object value : values) { if (value == null) { return true; } } return false; } } ================================================ FILE: extension/exousia/src/main/java/cloud/piranha/extension/exousia/AuthorizationPreFilter.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.exousia; import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN; import java.io.IOException; import jakarta.security.jacc.PolicyContext; import jakarta.servlet.FilterChain; import jakarta.servlet.FilterConfig; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.http.HttpFilter; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import cloud.piranha.core.api.FilterPriority; import cloud.piranha.core.api.SecurityManager; import cloud.piranha.core.api.WebApplication; /** * This filter is uses to call a Jakarta Authentication system module at the * start of an HTTP request. * *

* Note, this Filter *MUST* be installed as the first filter, and it should * *NOT* be possible to place a filter before this filter. The standard Servlet * API does not provide facilities for this. * * @author Arjan Tijms * */ public class AuthorizationPreFilter extends HttpFilter implements FilterPriority { /** * Stores the local servlet request. */ private static final ThreadLocal LOCAL_SERVLET_REQUEST = new ThreadLocal<>(); /** * Stores the priority. */ private static final int PRIORITY = 0; private static final long serialVersionUID = 8478463438252262094L; /** * Stores the security manager. */ private transient SecurityManager securityManager; /** * Constructor. */ public AuthorizationPreFilter() { } /** * Get the local servlet request. * * @return the local service request. */ public static ThreadLocal getLocalServletRequest() { return LOCAL_SERVLET_REQUEST; } @Override public int getPriority() { return PRIORITY; } @Override public void init(FilterConfig filterConfig) throws ServletException { securityManager = ((WebApplication) filterConfig.getServletContext()) .getManager().getSecurityManager(); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain); } @Override protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { WebApplication context = (WebApplication) request.getServletContext(); PolicyContext.setContextID(context.getServletContextId()); if (!securityManager.isRequestSecurityAsRequired(request, response)) { response.setStatus(SC_FORBIDDEN); return; } LOCAL_SERVLET_REQUEST.set(request); try { chain.doFilter(request, response); } finally { LOCAL_SERVLET_REQUEST.remove(); } } } ================================================ FILE: extension/exousia/src/main/java/cloud/piranha/extension/exousia/AuthorizationPreInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.exousia; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.impl.DefaultAuthenticatedIdentity; import jakarta.security.jacc.Policy; import jakarta.security.jacc.PolicyFactory; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import java.util.Set; import org.glassfish.exousia.AuthorizationService; import org.glassfish.exousia.modules.def.DefaultPolicyConfigurationFactory; import org.glassfish.exousia.modules.def.DefaultPolicyFactory; /** * The Exousia initializer. * * @author Arjan Tijms */ public class AuthorizationPreInitializer implements ServletContainerInitializer { /** * Defines the constant for the auth service attribute. */ public static final String AUTHZ_SERVICE = AuthorizationPreInitializer.class.getName() + ".authz.service"; /** * Defines the constant for the auth factory class attribute. */ public static final String AUTHZ_FACTORY_CLASS = AuthorizationPreInitializer.class.getName() + ".authz.factory.class"; /** * Defines the constant fore the auth policy class attribute. */ public static final String AUTHZ_POLICY_CLASS = AuthorizationPreInitializer.class.getName() + ".authz.module.class"; /** * Defines the constant for the unchecked permissions attribute. */ public static final String UNCHECKED_PERMISSIONS = AuthorizationPreInitializer.class.getName() + ".unchecked.permissions"; /** * Defines the constants for the per-role permissions attribute. */ public static final String PERROLE_PERMISSIONS = AuthorizationPreInitializer.class.getName() + ".perrole.permissions"; /** * Defines the constant for the security constraints attribute. */ public static final String CONSTRAINTS = AuthorizationPreInitializer.class.getName() + ".constraints"; /** * Defines the constant for the security elements attribute. */ public static final String SECURITY_ELEMENTS = AuthorizationPreInitializer.class.getName() + ".security.elements"; /** * Defines the constant for the security annotations attribute. */ public static final String SECURITY_ANNOTATIONS = "cloud.piranha.authorization.exousia.AuthorizationPreInitializer.security.annotations"; /** * Stores the Piranha to Exousia converter. */ PiranhaToExousiaConverter piranhaToExousiaConverter = new PiranhaToExousiaConverter(); /** * Constructor. */ public AuthorizationPreInitializer() { } /** * Initialize Exousia * * @param classes the classes. * @param servletContext the Servlet context. * @throws ServletException when a Servlet error occurs. */ @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { WebApplication context = (WebApplication) servletContext; // Create the main Exousia authorization service, which implements the various entry points (an SPI) // for a runtime to make use of Jakarta Authorization createAuthorizationService(context); } // ### Private methods private void createAuthorizationService(WebApplication context) throws ServletException { // Gets the authorization module classes that were configured externally Class factoryClass = getAttribute(context, AUTHZ_FACTORY_CLASS); Class policyClass = getAttribute(context, AUTHZ_POLICY_CLASS); PolicyFactory.setPolicyFactory(new DefaultPolicyFactory()); /* * Required since Exousia 3.0.0-M3. */ System.setProperty("jakarta.security.jacc.PolicyConfigurationFactory.provider", DefaultPolicyConfigurationFactory.class.getName()); // No need for the previous policy (likely the Java SE "JavaPolicy") to be consulted. // Policy.setPolicy(null); // Create the main Exousia authorization service, which implements the various entry points (an SPI) // for a runtime to make use of Jakarta Authorization AuthorizationService authorizationService = new AuthorizationService( factoryClass, policyClass, context.getServletContextId(), DefaultAuthenticatedIdentity::getCurrentSubject, () -> new PiranhaPrincipalMapper()); authorizationService.setRequestSupplier( context.getServletContextId(), () -> AuthorizationPreFilter.getLocalServletRequest().get()); context.setAttribute(AUTHZ_SERVICE, authorizationService); } // ### Utility methods private static T getAttribute(ServletContext servletContext, String name) throws ServletException { T t = getOptionalAttribute(servletContext, name); if (t == null) { throw new ServletException("Attribute " + name + " not specified"); } return t; } private static T getOptionalAttribute(ServletContext servletContext, String name) { @SuppressWarnings("unchecked") T t = (T) servletContext.getAttribute(name); return t; } } ================================================ FILE: extension/exousia/src/main/java/cloud/piranha/extension/exousia/PiranhaPrincipalMapper.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.exousia; import cloud.piranha.core.api.AuthenticatedIdentity; import jakarta.security.jacc.PrincipalMapper; import java.security.Principal; import java.util.Set; import java.util.stream.Collectors; import static java.util.stream.StreamSupport.stream; import javax.security.auth.Subject; /** * The Piranha PrincipalMapper. * * @author Arjan Tijms */ public class PiranhaPrincipalMapper implements PrincipalMapper { /** * Constructor. */ public PiranhaPrincipalMapper() { } @Override public Principal getCallerPrincipal(Subject sbjct) { return null; } @Override public Set getMappedRoles(Set principals) { return stream(principals.spliterator(), false) .filter(AuthenticatedIdentity.class::isInstance) .map(AuthenticatedIdentity.class::cast) .flatMap(e -> e.getGroups().stream()) .collect(Collectors.toSet()); } @Override public Set getMappedRoles(Subject sbjct) { return getMappedRoles(sbjct.getPrincipals()); } } ================================================ FILE: extension/exousia/src/main/java/cloud/piranha/extension/exousia/PiranhaToExousiaConverter.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.exousia; import cloud.piranha.core.api.SecurityManager; import cloud.piranha.core.api.SecurityWebResourceCollection; import cloud.piranha.core.api.WebApplication; import jakarta.servlet.ServletSecurityElement; import jakarta.servlet.annotation.ServletSecurity; import static jakarta.servlet.annotation.ServletSecurity.TransportGuarantee.CONFIDENTIAL; import static jakarta.servlet.annotation.ServletSecurity.TransportGuarantee.NONE; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.glassfish.exousia.constraints.SecurityConstraint; import org.glassfish.exousia.constraints.WebResourceCollection; import org.glassfish.exousia.constraints.transformer.ElementsToConstraintsTransformer; import org.glassfish.exousia.mapping.SecurityRoleRef; /** * This class converts from Piranha security types to Exousia security types. * * @author arjan * */ public class PiranhaToExousiaConverter { /** * Constructor. */ public PiranhaToExousiaConverter() { } /** * Get the security constraints from security elements. * * @param elements the security elements. * @return the security constraints. */ public List getConstraintsFromSecurityElements(List, ServletSecurityElement>> elements) { if (elements == null) { return null; } List constraints = new ArrayList<>(); for (Entry, ServletSecurityElement> elementEntry : elements) { constraints.addAll(ElementsToConstraintsTransformer.createConstraints( new HashSet<>(elementEntry.getKey()), elementEntry.getValue())); } return constraints; } /** * Get the security constraints from annotations. * * @param elements the elements. * @return the security constraints. */ public List getConstraintsFromSecurityAnnotations(List, ServletSecurity>> elements) { if (elements == null) { return null; } List constraints = new ArrayList<>(); for (Entry, ServletSecurity> elementEntry : elements) { constraints.addAll(ElementsToConstraintsTransformer.createConstraints( new HashSet<>(elementEntry.getKey()), elementEntry.getValue())); } return constraints; } /** * Get the security constraints from the security manager. * * @param securityManager the security manager. * @return the security constraints. */ public List getConstraintsFromSecurityManager(SecurityManager securityManager) { List constraints = new ArrayList<>(); for (cloud.piranha.core.api.SecurityConstraint xmlConstraint : securityManager.getSecurityConstraints()) { List webResourceCollections = new ArrayList<>(); for (SecurityWebResourceCollection xmlCollection : xmlConstraint.getSecurityWebResourceCollections()) { webResourceCollections.add(new WebResourceCollection( xmlCollection.getUrlPatterns(), xmlCollection.getHttpMethods(), xmlCollection.getHttpMethodOmissions())); } constraints.add(new SecurityConstraint( webResourceCollections, new HashSet<>(xmlConstraint.getRoleNames()), "confidential".equalsIgnoreCase(xmlConstraint.getTransportGuarantee()) ? CONFIDENTIAL : NONE)); } return constraints; } /** * Get the security role refs from the web application. * * @param servletNames the servlet names. * @param webApplication the web application. * @return the security role refs. */ public Map> getSecurityRoleRefsFromSecurityManager(Set servletNames, WebApplication webApplication) { Map> securityRoleRefs = new HashMap<>(); SecurityManager securityManager = webApplication.getManager().getSecurityManager(); for (String servletName : servletNames) { List securityRoleRefList = new ArrayList<>(); if (securityManager.getSecurityRoleReferences().get(servletName) != null) { securityManager.getSecurityRoleReferences().get(servletName).forEach( roleReference -> { SecurityRoleRef securityRole = new SecurityRoleRef( roleReference.getRoleName(), roleReference.getRoleLink()); securityRoleRefList.add(securityRole); } ); } securityRoleRefs.put(servletName, securityRoleRefList); } return securityRoleRefs; } } ================================================ FILE: extension/exousia/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Eclipse Exousia integration extension. * *

* This extension integrates Eclipse Exousia into Piranha. See * https://github.com/eclipse-ee4j/exousia for more information about its * project. *

* * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.exousia { exports cloud.piranha.extension.exousia; opens cloud.piranha.extension.exousia; requires cloud.piranha.core.api; requires cloud.piranha.core.impl; requires jakarta.security.jacc; requires jakarta.servlet; requires transitive org.glassfish.exousia; } ================================================ FILE: extension/expressly/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-expressly jar Piranha - Extension - Expressly cloud.piranha.core piranha-core-api ${project.version} compile org.glassfish.expressly expressly compile ================================================ FILE: extension/expressly/src/main/java/cloud/piranha/extension/expressly/ExpresslyExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.expressly; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import java.lang.System.Logger; import static java.lang.System.Logger.Level.TRACE; /** * The extension that delivers Expressly to Piranha. * * @author Manfred Riem (mriem@manorrock.com) */ public class ExpresslyExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(ExpresslyExtension.class.getName()); /** * Constructor. */ public ExpresslyExtension() { } /** * Configure the extension. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { LOGGER.log(TRACE, "Configuring Expressly extension"); } } ================================================ FILE: extension/expressly/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module integrates Exressly into Piranha. See * https://github.com/eclipse-ee4j/expressly for more information. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.expressly { requires cloud.piranha.core.api; requires transitive org.glassfish.expressly; } ================================================ FILE: extension/fileupload/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-fileupload jar Piranha - Extension - File Upload cloud.piranha.core piranha-core-impl ${project.version} compile org.apache.commons commons-fileupload2-jakarta-servlet6 compile org.junit.jupiter junit-jupiter-api test org.junit.platform junit-platform-launcher test default file:///tmp/piranha/extension/file-upload/ ================================================ FILE: extension/fileupload/src/main/java/cloud/piranha/extension/fileupload/FileUploadExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.fileupload; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import static java.lang.System.Logger.Level.TRACE; /** * The File Upload extension that configures the web application so it can * support file uploads. * * @author Manfred Riem (mriem@manorrock.com) */ public class FileUploadExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(FileUploadExtension.class.getName()); /** * Constructor. */ public FileUploadExtension() { } @Override public void configure(WebApplication webApplication) { LOGGER.log(TRACE, "Configuring File Upload extension"); webApplication.addInitializer(FileUploadMultiPartInitializer.class.getName()); } } ================================================ FILE: extension/fileupload/src/main/java/cloud/piranha/extension/fileupload/FileUploadMultiPart.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.fileupload; import jakarta.servlet.http.Part; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import org.apache.commons.fileupload2.core.FileItem; /** * The Part for the FileUploadMultiPartManager. * *

* This class implements the Servlet Part API and delegates to an Apache Commons * FileItem for its functionality. *

* * @author Manfred Riem (mriem@manorrock.com) */ public class FileUploadMultiPart implements Part { /** * Stores the item. */ private final FileItem item; /** * Constructor. * * @param item the file item. */ public FileUploadMultiPart(FileItem item) { this.item = item; } @Override public void delete() throws IOException { item.delete(); } @Override public String getContentType() { return item.getContentType(); } @Override public String getHeader(String name) { return item.getHeaders().getHeader(name); } @Override public Collection getHeaderNames() { Collection result = new ArrayList<>(); item.getHeaders().getHeaderNames().forEachRemaining(result::add); return result; } @Override public Collection getHeaders(String name) { Collection result = new ArrayList<>(); item.getHeaders().getHeaders(name).forEachRemaining(result::add); return result; } @Override public InputStream getInputStream() throws IOException { return item.getInputStream(); } @Override public String getName() { return item.getFieldName(); } @Override public long getSize() { return item.getSize(); } @Override public String getSubmittedFileName() { return item.getName(); } @Override public void write(String filename) throws IOException { try { item.write(new File(filename).toPath()); } catch (Exception e) { throw new IOException(e); } } } ================================================ FILE: extension/fileupload/src/main/java/cloud/piranha/extension/fileupload/FileUploadMultiPartInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.fileupload; import cloud.piranha.core.api.WebApplication; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import java.lang.System.Logger; import static java.lang.System.Logger.Level.TRACE; import java.util.Set; /** * The ServletContainerInitializer that is used to configure the web application * to support file upload: * *

* The ServletContainerInitializer performs the following steps: *

* *
    *
  1. Sets the MultiPartManager to an instance of * FileUploadMultiPartManager.
  2. *
  3. Adds the JakartaFileCleaner listener that cleans up the temporary * files.
  4. *
* * @author Manfred Riem (mriem@manorrock.com) */ public class FileUploadMultiPartInitializer implements ServletContainerInitializer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(FileUploadMultiPartInitializer.class.getName()); /** * Constructor. */ public FileUploadMultiPartInitializer() { } @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { WebApplication webApplication = (WebApplication) servletContext; if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Setting the MultiPartManager"); } webApplication.getManager().setMultiPartManager(new FileUploadMultiPartManager()); if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Adding the listener used to cleanup temporary files"); } webApplication.addListener("org.apache.commons.fileupload2.jakarta.servlet6.JakartaFileCleaner"); } } ================================================ FILE: extension/fileupload/src/main/java/cloud/piranha/extension/fileupload/FileUploadMultiPartManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.fileupload; import cloud.piranha.core.api.MultiPartManager; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationRequest; import jakarta.servlet.MultipartConfigElement; import static jakarta.servlet.ServletContext.TEMPDIR; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.Part; import java.io.File; import java.lang.System.Logger; import static java.lang.System.Logger.Level.TRACE; import static java.lang.System.Logger.Level.WARNING; import java.util.ArrayList; import java.util.Collection; import org.apache.commons.fileupload2.core.DiskFileItemFactory; import org.apache.commons.fileupload2.core.FileItem; import org.apache.commons.fileupload2.core.FileUploadException; import org.apache.commons.fileupload2.jakarta.servlet6.JakartaServletFileUpload; /** * The ApacheMultiPartManager. * *

* The FileUploadMultiPartManager implements the MultiPartManager API that * delivers file upload functionality to a web application by delegating to * Apache Commons File Upload. *

* * @author Manfred Riem (mriem@manorrock.com) */ public class FileUploadMultiPartManager implements MultiPartManager { /** * Stores the constant for the file size treshold. */ private static final String FILE_SIZE_THRESHOLD_NAME = "cloud.piranha.extension.fileupload.fileSizeTreshold"; /** * Stores the constant for the output directory. */ private static final String OUTPUT_DIRECTORY_NAME = "cloud.piranha.extension.fileupload.outputDirectory"; /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(FileUploadMultiPartManager.class.getName()); /** * Constructor. */ public FileUploadMultiPartManager() { } @SuppressWarnings("unchecked") @Override public Collection getParts(WebApplication webApplication, WebApplicationRequest request) throws ServletException { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Getting parts for request: {0}", request); } if (!JakartaServletFileUpload.isMultipartContent(request)) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Request: {0} is not a multipart/form-date request"); } throw new ServletException("Not a multipart/form-data request"); } Collection parts = (Collection) request.getAttribute(Part.class.getName()); if (parts == null) { Collection newParts = new ArrayList<>(); try { setupFileUpload(webApplication, request.getMultipartConfig()) .parseRequest((HttpServletRequest) request) .forEach(item -> newParts.add(new FileUploadMultiPart((FileItem) item))); } catch (FileUploadException fue) { LOGGER.log(WARNING, "Error getting part", fue); } request.setAttribute(Part.class.getName(), newParts); parts = newParts; } return parts; } @Override public Part getPart(WebApplication webApplication, WebApplicationRequest request, String name) throws ServletException { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Getting part: {0} for request: {1}", name, request); } if (!JakartaServletFileUpload.isMultipartContent(request)) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Request: {0} is not a multipart/form-date request"); } throw new ServletException("Not a multipart/form-data request"); } for (Part part : getParts(webApplication, request)) { if (part.getName().equals(name)) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Found part: {0}", part.getName()); } return part; } } if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Unable to find part: {0}", name); } return null; } /** * Setup the JakartaServletFileUpload instance. * * @param webApplication the web application. * @param multipartConfig the multi-part config element. * @return the JakartServletFileUpload instance. */ private synchronized JakartaServletFileUpload setupFileUpload(WebApplication webApplication, MultipartConfigElement multipartConfig) { JakartaServletFileUpload upload = (JakartaServletFileUpload) webApplication.getAttribute(FileUploadMultiPartManager.class.getName()); if (upload == null) { /* * Default to TEMPDIR. */ File outputDirectory = (File) webApplication.getAttribute(TEMPDIR); /* * If the multipart config has a location use it. If it is relative * use the TEMPDIR as the parent directory. */ if (multipartConfig.getLocation() != null && !multipartConfig.getLocation().isEmpty()) { File location = new File(multipartConfig.getLocation()); if (!location.isAbsolute()) { location = ((File) webApplication.getAttribute(TEMPDIR)).toPath().resolve(location.toPath()).toFile(); } outputDirectory = location; } /* * If OUTPUT_DIRECTORY_NAME is set use it. */ if (webApplication.getInitParameter(OUTPUT_DIRECTORY_NAME) != null) { outputDirectory = new File(webApplication.getInitParameter(OUTPUT_DIRECTORY_NAME)); } /* * Default to 10240 (10 KB). */ int sizeThreshold = 10240; /* * If the multipart config has a size > 0 use it. */ if (multipartConfig.getFileSizeThreshold() != 0) { sizeThreshold = multipartConfig.getFileSizeThreshold(); } /* * If FILE_SIZE_THRESHOLD_NAME is set use it. */ if (webApplication.getInitParameter(FILE_SIZE_THRESHOLD_NAME) != null) { try { sizeThreshold = Integer.parseInt(webApplication.getInitParameter("cloud.piranha.extension.fileupload.fileSizeTreshold")); } catch (NumberFormatException nfe) { // ignore and let defaults apply. } } DiskFileItemFactory factory = DiskFileItemFactory .builder() .setBufferSize(sizeThreshold) .setFile(outputDirectory) .get(); upload = new JakartaServletFileUpload(factory); webApplication.setAttribute(FileUploadMultiPartManager.class.getName(), upload); } return upload; } } ================================================ FILE: extension/fileupload/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the file upload functionality using Apache Commons * FileUpload. See https://github.com/apache/commons-fileupload for more * information. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.fileupload { exports cloud.piranha.extension.fileupload; opens cloud.piranha.extension.fileupload; requires cloud.piranha.core.api; requires org.apache.commons.fileupload2.core; requires org.apache.commons.fileupload2.jakarta.servlet6; requires org.apache.commons.io; } ================================================ FILE: extension/fileupload/src/site/markdown/index.md ================================================ # Piranha File Upload Extension The File Upload extension delivers the ability for a Piranha runtime to support file upload. This extension is available by default for the following runtimes: 1. Piranha Server 1. Piranha Servlet 1. Piranha Web Profile ## Configuration parameters The following configuration parameters are available: 1. `cloud.piranha.extension.fileupload.outputDirectory` - the directory where the file upload will store temporary files. The default is the location as 1. `cloud.piranha.extension.fileupload.fileSizeTreshold` - the file size threshold (in bytes) before the runtime will create a temporary file on the filesystem to store the upload. The default is 10240 (10 KB). ## Setting the configuration parameters using web.xml You can set the configuration parameter in the web.xml as illustrated below: ```xml cloud.piranha.extension.fileupload.fileSizeTreshold 1048576 ``` ================================================ FILE: extension/fileupload/src/site/site.xml ================================================ ================================================ FILE: extension/fileupload/src/test/java/cloud/piranha/extension/fileupload/tests/FileUploadMultiPartManagerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.fileupload.tests; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.core.impl.DefaultWebApplicationRequest; import cloud.piranha.extension.fileupload.FileUploadMultiPartManager; import jakarta.servlet.MultipartConfigElement; import java.io.File; import static org.junit.jupiter.api.Assertions.assertNull; import org.junit.jupiter.api.Test; /** * The JUnit tests for the FileUploadMultiPartManager class. * * @author Manfred Riem (mriem@manorrock.com) */ class FileUploadMultiPartManagerTest { /** * Test getPart method. * * @throws Exception when a serious error occurs. */ @Test void testGetPart() throws Exception { DefaultWebApplication application = new DefaultWebApplication(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setMultipartConfig(new MultipartConfigElement(new File("target").getAbsolutePath())); request.setContentType("multipart/form-data"); request.setMethod("POST"); FileUploadMultiPartManager manager = new FileUploadMultiPartManager(); assertNull(manager.getPart(application, request, "part_test")); } } ================================================ FILE: extension/fileupload/src/test/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the tests for the File Upload extension. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.fileupload.tests { exports cloud.piranha.extension.fileupload.tests; opens cloud.piranha.extension.fileupload.tests; requires cloud.piranha.core.impl; requires cloud.piranha.extension.fileupload; requires org.junit.jupiter.api; } ================================================ FILE: extension/handlestypes/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-handlestypes jar Piranha - Extension - Handles Types cloud.piranha.core piranha-core-api ${project.version} provided cloud.piranha.core piranha-core-impl ${project.version} test org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test maven-compiler-plugin testCompile test-compile false org.apache.maven.plugins maven-surefire-plugin false ================================================ FILE: extension/handlestypes/src/main/java/cloud/piranha/extension/handlestypes/HandlesTypesExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.handlestypes; import cloud.piranha.extension.handlestypes.internal.InternalHandlesTypesManager; import cloud.piranha.extension.handlestypes.internal.InternalHandlesTypesInitializer; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; /** * The HandlesTypes extension delivers a HandlesTypes manager. * * @author Manfred Riem (mriem@manorrock.com) */ public class HandlesTypesExtension implements WebApplicationExtension { /** * Constructor. */ public HandlesTypesExtension() { } /** * Configure the web application. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { webApplication.getManager().setHandlesTypesManager( new InternalHandlesTypesManager()); webApplication.addInitializer(new InternalHandlesTypesInitializer()); } } ================================================ FILE: extension/handlestypes/src/main/java/cloud/piranha/extension/handlestypes/internal/InternalHandlesTypesInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.handlestypes.internal; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationClassLoader; import cloud.piranha.resource.api.ResourceManager; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.HandlesTypes; import java.lang.System.Logger; import static java.lang.System.Logger.Level.ERROR; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * The ServletContainerInitializer that delivers HandlesTypes processing. * * @author Manfred Riem (mriem@manorrock.com) */ public class InternalHandlesTypesInitializer implements ServletContainerInitializer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(InternalHandlesTypesInitializer.class.getName()); @Override public void onStartup(Set> classes, ServletContext context) throws ServletException { WebApplication webApplication = (WebApplication) context; if (webApplication != null) { /* * Determine the set of classes for all the ServletContainerInitializers. */ HashSet> collectedClasses = new HashSet<>(); List initializers = webApplication.getInitializers(); for (ServletContainerInitializer initializer : initializers) { if (initializer.getClass().isAnnotationPresent(HandlesTypes.class)) { HandlesTypes handlesTypes = initializer.getClass().getAnnotation(HandlesTypes.class); if (handlesTypes.value() != null) { collectedClasses.addAll( Arrays.stream(handlesTypes.value()) .collect(Collectors.toSet())); } } } /* * Walk the resource hierarchy and determine if the collected * classes are used (as annotation, interface or extends) and if so * add them to the HandlesTypes manager. */ WebApplicationClassLoader classLoader = (WebApplicationClassLoader) webApplication.getClassLoader(); if (classLoader != null) { ResourceManager resourceManager = classLoader.getResourceManager(); resourceManager.getAllLocations().parallel() .forEach(location -> { Class clazz = loadClass(webApplication, location); if (clazz != null) { processClass(webApplication, collectedClasses, clazz); } }); } else { LOGGER.log(ERROR, "Unable to process HandlesTypes because the classloader is incompatible"); } } } /** * Load a class from the given location. * * @param webApplication the web application. * @param location the location of the class file. * @return the loaded class, or null if it could not be loaded. */ private Class loadClass(WebApplication webApplication, String location) { /* * We do not look at module-info.java. */ if (location.toLowerCase().endsWith("module-info.class")) { return null; } /* * We do not look at any location that is not referring to a class. */ if (!location.toLowerCase().endsWith(".class")) { return null; } String className = location.substring(1).replace('/', '.'); className = className.substring(0, className.lastIndexOf('.')); try { return webApplication.getClassLoader().loadClass(className); } catch (Throwable t) { /* * If we could not load it we cannot determine if we are a match. */ return null; } } /** * Process the class to check if it matches any collected classes. * * @param webApplication the web application. * @param collectedClasses the collected classes. * @param clazz the class under consideration. */ private void processClass(WebApplication webApplication, HashSet> collectedClasses, Class clazz) { for (Class collectedClass : collectedClasses) { if (isClassAnnotatedWith(clazz, collectedClass)) { webApplication.getManager().getHandlesTypesManager() .addAnnotatedClass(collectedClass, clazz); } if (isClassExtending(clazz, collectedClass)) { webApplication.getManager().getHandlesTypesManager() .addExtendingClass(collectedClass, clazz); } if (isClassImplementing(clazz, collectedClass)) { webApplication.getManager().getHandlesTypesManager() .addImplementingClass(collectedClass, clazz); } } } /** * Check if the class is annotated with the given annotation. * * @param clazz the class under consideration. * @param annotationClass the annotation class. * @return true if it is, false otherwise. */ private boolean isClassAnnotatedWith(Class clazz, Class annotationClass) { return annotationClass.isAnnotation() && clazz.getAnnotation(annotationClass) != null; } /** * Check if the class extends the given superclass. * * @param clazz the class. * @param superClass the class being extended (aka the super class). * @return true if it is, false otherwise. */ private boolean isClassExtending(Class clazz, Class superClass) { Class currentClass = clazz.getSuperclass(); while (currentClass != null) { if (superClass.equals(currentClass)) { return true; } currentClass = currentClass.getSuperclass(); } return false; } /** * Check if the class implements the given interface. * * @param clazz the class. * @param interfaceClass the interface class. * @return true if it is, false otherwise. */ private boolean isClassImplementing(Class clazz, Class interfaceClass) { Class currentClass = clazz; while (currentClass != null) { for (Class iface : currentClass.getInterfaces()) { if (interfaceClass.isAssignableFrom(iface)) { return true; } } currentClass = currentClass.getSuperclass(); } return false; } } ================================================ FILE: extension/handlestypes/src/main/java/cloud/piranha/extension/handlestypes/internal/InternalHandlesTypesManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.handlestypes.internal; import cloud.piranha.core.api.HandlesTypesManager; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * The HandlesTypes manager. * * @author Manfred Riem (mriem@manorrock.com) */ public class InternalHandlesTypesManager implements HandlesTypesManager { /** * Stores the annotated classes. */ private final ConcurrentHashMap, Set>> annotatedClasses = new ConcurrentHashMap<>(); /** * Stores the extending classes. */ private final ConcurrentHashMap, Set>> extendingClasses = new ConcurrentHashMap<>(); /** * Stores the implementing classes. */ private final ConcurrentHashMap, Set>> implementingClasses = new ConcurrentHashMap<>(); /** * Constructor. */ public InternalHandlesTypesManager() { } @Override public void addAnnotatedClass(Class annotationClass, Class annotatedClass) { synchronized (annotatedClasses) { if (!annotatedClasses.containsKey(annotationClass)) { HashSet> hashSet = new HashSet<>(); hashSet.add(annotatedClass); annotatedClasses.put(annotationClass, hashSet); } else { annotatedClasses.get(annotationClass).add(annotatedClass); } } } @Override public void addExtendingClass(Class baseClass, Class extendingClass) { synchronized (extendingClasses) { if (!extendingClasses.containsKey(baseClass)) { HashSet> hashSet = new HashSet<>(); hashSet.add(extendingClass); extendingClasses.put(baseClass, hashSet); } else { extendingClasses.get(baseClass).add(extendingClass); } } } @Override public void addImplementingClass(Class interfaceClass, Class implementingClass) { synchronized (implementingClasses) { if (!implementingClasses.containsKey(interfaceClass)) { HashSet> hashSet = new HashSet<>(); hashSet.add(implementingClass); implementingClasses.put(interfaceClass, hashSet); } else { implementingClasses.get(interfaceClass).add(implementingClass); } } } @Override public Set> getAnnotatedClasses(Class annotationClass) { return annotatedClasses.getOrDefault(annotationClass, new HashSet<>()); } @Override public Set> getExtendingClasses(Class baseClass) { return extendingClasses.getOrDefault(baseClass, new HashSet<>()); } @Override public Set> getImplementingClasses(Class interfaceClass) { return implementingClasses.getOrDefault(interfaceClass, new HashSet<>()); } @Override public Set> getClasses(Set> classes) { HashSet> result = new HashSet<>(); if (classes != null) { for (Class clazz : classes) { result.addAll(getAnnotatedClasses(clazz)); result.addAll(getExtendingClasses(clazz)); result.addAll(getImplementingClasses(clazz)); } } return result.isEmpty() ? null : result; } } ================================================ FILE: extension/handlestypes/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * The module that delivers HandlesTypes annotation support. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.handlestypes { exports cloud.piranha.extension.handlestypes; opens cloud.piranha.extension.handlestypes; requires transitive cloud.piranha.core.api; requires java.logging; } ================================================ FILE: extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/HandlesTypesExtensionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.handlestypes; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.impl.DefaultWebApplication; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * The JUnit tests for the HandlesTypesExtension class. * * @author Manfred Riem (mriem@manorrock.com) */ public class HandlesTypesExtensionTest { /** * Test configure method. */ @Test public void testConfigure() { WebApplication webApplication = new DefaultWebApplication(); HandlesTypesExtension extension = new HandlesTypesExtension(); extension.configure(webApplication); assertNotNull(webApplication.getManager().getHandlesTypesManager()); } } ================================================ FILE: extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/InternalHandlesTypesInitializerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.handlestypes.internal; import cloud.piranha.core.api.HandlesTypesManager; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.core.impl.DefaultWebApplicationClassLoader; import cloud.piranha.extension.handlestypes.HandlesTypesExtension; import cloud.piranha.resource.impl.AliasedDirectoryResource; import cloud.piranha.resource.impl.DefaultResourceManager; import java.io.File; import java.util.HashSet; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * The JUnit tests for the InternalHandlesTypesInitializer class. * * @author Manfred Riem (mriem@manorrock.com) */ public class InternalHandlesTypesInitializerTest { /** * Test onStartup method. */ @Test public void testOnStartup() throws Exception { DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(); DefaultResourceManager resourceManager = new DefaultResourceManager(); resourceManager.addResource(new AliasedDirectoryResource( new File("target/test-classes"), "/WEB-INF/classes")); classLoader.setResourceManager(resourceManager); DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setClassLoader(classLoader); HandlesTypesExtension extension = new HandlesTypesExtension(); extension.configure(webApplication); webApplication.addInitializer(new TestServletContainerInitializer()); webApplication.initialize(); /* * Verify a HandlesTypesManager was installed. */ HandlesTypesManager manager = webApplication.getManager().getHandlesTypesManager(); assertNotNull(manager); /* * Verify that for annotation TestB we have A listed as a class of interest. */ assertFalse(manager.getAnnotatedClasses(TestB.class).isEmpty()); /* * Verify that for super class TestC we have A listed as a class of interest. */ assertFalse(manager.getExtendingClasses(TestC.class).isEmpty()); /* * Verify that for interface TestD we have A listed as a class of interest. */ assertFalse(manager.getImplementingClasses(TestD.class).isEmpty()); /* * Verify that we can get a combined set and it should be not empty. */ HashSet> classes = new HashSet<>(); classes.add(TestB.class); classes.add(TestC.class); classes.add(TestD.class); assertFalse(manager.getClasses(classes).isEmpty()); } } ================================================ FILE: extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/InternalHandlesTypesManagerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.handlestypes.internal; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * The JUnit tests for the InternalHandlesTypesManager class. * * @author Manfred Riem (mriem@manorrock.com) */ public class InternalHandlesTypesManagerTest { /** * Test getClasses method. */ @Test public void testGetClasses() { InternalHandlesTypesManager manager = new InternalHandlesTypesManager(); assertNull(manager.getClasses(null)); } } ================================================ FILE: extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestA.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR TestA PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.handlestypes.internal; /** * Test class. * * @author Manfred Riem (mriem@manorrock.com) */ @TestB public class TestA extends TestC implements TestD { } ================================================ FILE: extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestB.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.handlestypes.internal; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Test annotation. * * @author Manfred Riem (mriem@manorrock.com) */ @Retention(RetentionPolicy.RUNTIME) public @interface TestB { } ================================================ FILE: extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestC.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR TestA PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.handlestypes.internal; /** * Test class. * * @author Manfred Riem (mriem@manorrock.com) */ public class TestC { } ================================================ FILE: extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestD.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR TestA PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.handlestypes.internal; /** * A test interface. * * @author Manfred Riem (mriem@manorrock.com) */ public interface TestD { } ================================================ FILE: extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestServletContainerInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.handlestypes.internal; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.HandlesTypes; import java.util.Set; /** * A test ServletContainerInitializer. * * @author Manfred Riem (mriem@manorrock.com) */ @HandlesTypes(value = { TestB.class, TestC.class, TestD.class }) public class TestServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(Set> c, ServletContext ctx) throws ServletException { } } ================================================ FILE: extension/hazelcast/README.md ================================================ # Piranha HTTP Session - Hazelcast Integration The Piranha HTTP Session - Hazelcast integration module is a module that can be used to have Hazelcast be the session manager. ## How to use? webApplication.setHttpSessionManager(new HazelcastHttpSessionManager()); This snippet above makes your web application use the Hazelcast HTTP session manager. ================================================ FILE: extension/hazelcast/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-hazelcast jar Piranha - Extension - Hazelcast com.hazelcast hazelcast compile cloud.piranha.core piranha-core-impl ${project.version} provided org.junit.jupiter junit-jupiter-api test ================================================ FILE: extension/hazelcast/src/main/java/cloud/piranha/extension/hazelcast/HazelcastHttpSession.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.hazelcast; import cloud.piranha.core.api.HttpSessionManager; import java.io.Serializable; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import jakarta.servlet.ServletContext; import jakarta.servlet.http.HttpSession; /** * The Hazelcast HttpSession. * * @author Manfred Riem (mriem@manorrock.com) */ public class HazelcastHttpSession implements HttpSession, Serializable { /** * Stores the attributes. */ private HashMap attributes = new HashMap<>(); /** * Stores the creation time. */ private long creationTime; /** * Stores the session id. */ private String id; /** * Stores the last accessed time. */ private long lastAccessedTime; /** * Stores the max inactive interval. */ private int maxInactiveInterval; /** * Stores if the session is new. */ private boolean newFlag; /** * Stores the servlet context. */ private transient ServletContext servletContext; /** * Stores the HTTP session manager. */ private transient HttpSessionManager sessionManager; /** * Stores the valid flag. */ private boolean valid; /** * Constructor. */ public HazelcastHttpSession() { } /** * Constructor. * * @param servletContext the servlet context. */ public HazelcastHttpSession(ServletContext servletContext) { this.servletContext = servletContext; this.creationTime = System.currentTimeMillis(); this.lastAccessedTime = System.currentTimeMillis(); this.valid = true; } /** * Constructor. * * @param servletContext the servlet context. * @param id the id. * @param newFlag the new flag. */ public HazelcastHttpSession(ServletContext servletContext, String id, boolean newFlag) { this.servletContext = servletContext; this.id = id; this.newFlag = newFlag; this.creationTime = System.currentTimeMillis(); this.lastAccessedTime = System.currentTimeMillis(); this.valid = true; } /** * {@return the attribute value} * @param name the attribute name. * @see HttpSession#getAttribute(java.lang.String) */ @Override public Object getAttribute(String name) { verifyValid("getAttribute"); return this.attributes.get(name); } /** * {@return the attribute names} * @see HttpSession#getAttributeNames() */ @Override public Enumeration getAttributeNames() { verifyValid("getAttributeNames"); return Collections.enumeration(attributes.keySet()); } /** * {@return the creation time} * @see HttpSession#getCreationTime() */ @Override public long getCreationTime() { verifyValid("getCreationTime"); return this.creationTime; } /** * {@return the id} * @see HttpSession#getId() */ @Override public String getId() { return this.id; } /** * {@return the last accessed time} * @see HttpSession#getLastAccessedTime() */ @Override public long getLastAccessedTime() { verifyValid("getLastAccessedTime"); return this.lastAccessedTime; } /** * {@return the maximum inactive interval} * @see HttpSession#getMaxInactiveInterval() */ @Override public int getMaxInactiveInterval() { return this.maxInactiveInterval; } /** * {@return the servlet context} * @see HttpSession#getServletContext() */ @Override public ServletContext getServletContext() { return servletContext; } /** * Invalidate the session. * * @see HttpSession#invalidate() */ @Override public void invalidate() { verifyValid("invalidate"); this.valid = false; } /** * Is the session new. * * @return true if it is, false otherwise. * @see HttpSession#isNew() */ @Override public boolean isNew() { verifyValid("isNew"); return this.newFlag; } /** * Remove the attribute. * * @param name the attribute name. * @see HttpSession#removeAttribute(java.lang.String) */ @Override public void removeAttribute(String name) { verifyValid("removeAttribute"); sessionManager.attributeRemoved(this, name, this.attributes.remove(name)); } /** * Set the attribute. * * @param name the attribute name. * @param value the attribute value. * @see HttpSession#setAttribute(java.lang.String, java.lang.Object) */ @Override public void setAttribute(String name, Object value) { verifyValid("setAttribute"); if (value != null) { boolean added = true; if (attributes.containsKey(name)) { added = false; } Object oldValue = attributes.put(name, (Serializable) value); if (added) { sessionManager.attributeAdded(this, name, value); } else { sessionManager.attributeReplaced(this, name, oldValue, value); } } else { removeAttribute(name); } } /** * Set the id. * * @param id the id. */ public void setId(String id) { this.id = id; } /** * Set the maximum inactive interval. * * @param maxInactiveInterval the maximum inactive interval. * @see HttpSession#setMaxInactiveInterval(int) */ @Override public void setMaxInactiveInterval(int maxInactiveInterval) { this.maxInactiveInterval = maxInactiveInterval; } /** * Set the new flag. * * @param newFlag the new flag. */ public void setNew(boolean newFlag) { verifyValid("setNew"); this.newFlag = newFlag; } /** * Set the servlet context. * * @param servletContext the servlet context. */ public void setServletContext(ServletContext servletContext) { this.servletContext = servletContext; } /** * Set the HTTP session manager. * * @param sessionManager the HTTP session manager. */ public void setSessionManager(HttpSessionManager sessionManager) { this.sessionManager = sessionManager; } /** * Verify if the session is valid. * * @param methodName the method name. */ private void verifyValid(String methodName) { if (!valid) { throw new IllegalStateException("Session is invalid, called by: " + methodName); } } } ================================================ FILE: extension/hazelcast/src/main/java/cloud/piranha/extension/hazelcast/HazelcastHttpSessionManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.hazelcast; import com.hazelcast.config.Config; import com.hazelcast.core.Hazelcast; import com.hazelcast.core.HazelcastInstance; import cloud.piranha.core.impl.DefaultHttpSessionManager; import java.util.UUID; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpSessionEvent; /** * The Hazelcast HTTP session manager. * * @author Manfred Riem (mriem@manorrock.com) */ public class HazelcastHttpSessionManager extends DefaultHttpSessionManager { /** * Stores the hazelcast instance. */ private HazelcastInstance hazelcast; /** * Constructor. */ public HazelcastHttpSessionManager() { this(HazelcastHttpSessionManager.class.getName()); } /** * Constructor. * * @param name the name used for the hazelcast session map. */ public HazelcastHttpSessionManager(String name) { super(); Config config = new Config(); config.setInstanceName(name); hazelcast = Hazelcast.newHazelcastInstance(); sessions = hazelcast.getMap(name); } /** * Change the session id. * * @param request the request. * @return the session id. */ @Override public String changeSessionId(HttpServletRequest request) { HttpSession session = request.getSession(false); String key = null; if (session != null) { String oldSessionId = session.getId(); sessions.remove(oldSessionId); key = UUID.randomUUID().toString(); HazelcastHttpSession newSession = (HazelcastHttpSession) session; newSession.setId(key); sessions.put(key, newSession); idListeners.stream().forEach(idListener -> idListener.sessionIdChanged(new HttpSessionEvent(newSession), oldSessionId)); } else { throw new IllegalStateException("No session active"); } return key; } @Override public synchronized HttpSession createSession(HttpServletRequest request) { String key = UUID.randomUUID().toString(); while(sessions.containsKey(key)) { key = UUID.randomUUID().toString(); } HazelcastHttpSession result = new HazelcastHttpSession(webApplication, key, true); result.setSessionManager(this); sessions.put(key, result); HttpServletResponse response = (HttpServletResponse) webApplication.getResponse(request); Cookie cookie = new Cookie(name, key); response.addCookie(cookie); sessionListeners.stream().forEach(sessionListener -> sessionListener.sessionCreated(new HttpSessionEvent(result))); return result; } @Override public HttpSession getSession(HttpServletRequest request, String currentSessionId) { HazelcastHttpSession result; HttpServletResponse response = (HttpServletResponse) webApplication.getResponse(request); result = (HazelcastHttpSession) sessions.get(currentSessionId); result.setSessionManager(this); result.setServletContext(webApplication); Cookie cookie = new Cookie(name, currentSessionId); response.addCookie(cookie); result.setNew(false); return result; } } ================================================ FILE: extension/hazelcast/src/main/java/cloud/piranha/extension/hazelcast/HazelcastInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.hazelcast; import cloud.piranha.core.api.WebApplication; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import java.util.Set; /** * The Hazelcast initializer. * * @author Thiago Henrique Hupner * @author Manfred Riem (mriem@manorrock.com) */ public class HazelcastInitializer implements ServletContainerInitializer { /** * Constructor. */ public HazelcastInitializer() { } @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { WebApplication webApplication = (WebApplication) servletContext; HazelcastHttpSessionManager sessionManager = new HazelcastHttpSessionManager(); sessionManager.setWebApplication(webApplication); webApplication.getManager().setHttpSessionManager(sessionManager); } } ================================================ FILE: extension/hazelcast/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ import cloud.piranha.extension.hazelcast.HazelcastInitializer; import jakarta.servlet.ServletContainerInitializer; /** * This module delivers the Hazelcast HttpSession integration extension. * *

* This extension integrates Hazelcast as a HttpSessionManager into Piranha. * See https://github.com/hazelcast/hazelcast for more information about * its project. *

* * @author Arjan Tijms */ module cloud.piranha.extension.hazelcast { exports cloud.piranha.extension.hazelcast; opens cloud.piranha.extension.hazelcast; provides ServletContainerInitializer with HazelcastInitializer; requires cloud.piranha.core.api; requires cloud.piranha.core.impl; requires com.hazelcast.core; } ================================================ FILE: extension/hazelcast/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer ================================================ cloud.piranha.extension.hazelcast.HazelcastInitializer ================================================ FILE: extension/hazelcast/src/test/java/cloud/piranha/extension/hazelcast/HazelcastInitializerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.hazelcast; import cloud.piranha.core.impl.DefaultWebApplication; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the HazelcastInitializer. * * @author Manfred Riem (mriem@manorrock.com) */ class HazelcastInitializerTest { @Test void testInitializer() { DefaultWebApplication webApp = new DefaultWebApplication(); webApp.addInitializer(new HazelcastInitializer()); webApp.initialize(); assertTrue(webApp.getManager().getHttpSessionManager() instanceof HazelcastHttpSessionManager); } } ================================================ FILE: extension/herring/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-herring jar Piranha - Extension - Herring cloud.piranha.core piranha-core-api ${project.version} compile com.manorrock.herring herring compile com.manorrock.herring herring-thread compile jakarta.annotation jakarta.annotation-api compile jakarta.enterprise jakarta.enterprise.cdi-api cloud.piranha piranha-embedded ${project.version} test ================================================ FILE: extension/herring/src/main/java/cloud/piranha/extension/herring/HerringExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.herring; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import com.manorrock.herring.DefaultInitialContext; import jakarta.annotation.Resource; import static java.lang.System.Logger.Level.DEBUG; import static java.lang.System.Logger.Level.WARNING; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Optional; import javax.naming.CompositeName; import javax.naming.Context; import static javax.naming.Context.INITIAL_CONTEXT_FACTORY; import javax.naming.NamingException; import javax.naming.Reference; import javax.naming.spi.NamingManager; /** * The WebApplicationExtension that is responsible for setting up the proper * Context instance so it can be made available during web application * initialization and subsequently during request processing as well as * delivering listeners to set/remove the Context from the current thread. * * @author Manfred Riem (mriem@manorrock.com) */ public class HerringExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(HerringExtension.class.getName()); /** * Constructor. */ public HerringExtension() { } /** * Configure the web application. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { LOGGER.log(DEBUG, "Configuring the HerringExtension"); if (Boolean.parseBoolean(System.getProperty("cloud.piranha.extension.herring.HerringExtension.enabled", "true"))) { if (System.getProperty(INITIAL_CONTEXT_FACTORY) == null) { LOGGER.log(DEBUG, "Setting " + INITIAL_CONTEXT_FACTORY + " to " + HerringInitialContextFactory.class.getName()); System.setProperty(INITIAL_CONTEXT_FACTORY, HerringInitialContextFactory.class.getName()); } if (!System.getProperty(INITIAL_CONTEXT_FACTORY).equals(HerringInitialContextFactory.class.getName())) { LOGGER.log(WARNING, INITIAL_CONTEXT_FACTORY + " is not set to " + HerringInitialContextFactory.class.getName()); } Context context = new DefaultInitialContext(); Context proxyContext = (Context) Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(), new Class[]{Context.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { Object returnValue = null; try { returnValue = method.invoke(context, args); } catch (InvocationTargetException e) { boolean invoked = false; try { if (method.getName().equals("lookup") && e.getCause() instanceof NamingException) { String jndiName = args[0].toString(); if (jndiName.startsWith("java:comp/env/")) { String classNameWithField = jndiName.substring("java:comp/env/".length()); String[] classNameAndField = classNameWithField.split("/"); if (classNameAndField.length == 2) { String className = classNameAndField[0]; String fieldName = classNameAndField[1]; Class beanClass = Class.forName(className, false, Thread.currentThread().getContextClassLoader()); Resource[] resources = null; Class type = null; try { Field beanField = beanClass.getDeclaredField(fieldName); resources = beanField.getAnnotationsByType(Resource.class); type = beanField.getType(); } catch (NoSuchFieldException | SecurityException exception) { char[] chars = fieldName.toCharArray(); chars[0] = Character.toUpperCase(chars[0]); String methodName = "set" + new String(chars); Optional optionalMethod = Arrays.stream(beanClass.getDeclaredMethods()) .filter(m -> m.getName().equals(methodName)) .filter(m -> m.getParameterCount() == 1) .filter(m -> m.getAnnotationsByType(Resource.class) != null) .findFirst(); // ignore overloaded for now if (optionalMethod.isPresent()) { resources = optionalMethod.get().getAnnotationsByType(Resource.class); type = optionalMethod.get().getParameterTypes()[0]; } } if (resources != null && resources.length > 0) { Resource resourceAnnnotation = resources[0]; String lookup = resourceAnnnotation.lookup(); if (!"".equals(lookup)) { returnValue = method.invoke(context, new Object[]{lookup}); args = new Object[]{lookup}; invoked = true; } else { throw new IllegalStateException("Cannot find " + type); } } } } } } catch (Throwable t) { if (t instanceof InvocationTargetException invocationException && invocationException.getTargetException() instanceof NamingException namingException) { throw namingException; } e.addSuppressed(t); } if (!invoked) { throw e; } } // De-referencing can eventually be moved to DefaultInitialContext if (method.getName().equals("lookup") && returnValue instanceof Reference) { returnValue = NamingManager.getObjectInstance( returnValue, new CompositeName(args[0].toString()), null, null); } return returnValue; } catch (InvocationTargetException e) { throw e.getCause(); } } }); HerringInitialContextFactory.setInitialContext(proxyContext); webApplication.setAttribute(Context.class.getName(), proxyContext); } } } ================================================ FILE: extension/herring/src/main/java/cloud/piranha/extension/herring/HerringInitialContextFactory.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.herring; import com.manorrock.herring.DefaultInitialContext; import java.util.Hashtable; import javax.naming.Context; import javax.naming.NamingException; import javax.naming.spi.InitialContextFactory; /** * The Naming InitialContextFactory. * * @author Manfred Riem (mriem@manorrock.com) */ public class HerringInitialContextFactory implements InitialContextFactory { /** * Stores the initial context. */ private static Context theOneAndOnlyInstance = new DefaultInitialContext(); /** * Constructor. */ public HerringInitialContextFactory() { } /** * Sets the initial context * * @param context the initial context */ public static void setInitialContext(Context context) { theOneAndOnlyInstance = context; } /** * Get the initial context. * * @return the initial context. * @param environment the environment. * @throws NamingException when a naming error occurs. */ @Override public Context getInitialContext(Hashtable environment) throws NamingException { return theOneAndOnlyInstance; } } ================================================ FILE: extension/herring/src/main/java/cloud/piranha/extension/herring/HerringServletRequestListener.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.herring; import cloud.piranha.core.api.WebApplication; import com.manorrock.herring.thread.ThreadInitialContextFactory; import jakarta.servlet.ServletRequestEvent; import jakarta.servlet.ServletRequestListener; import static java.lang.System.Logger.Level.DEBUG; import javax.naming.Context; /** * The ServletRequestListener that sets the Context instance on the current * thread just before request processing and removes the Context instance from * the current after the request has been processed. * * @author Manfred Riem (mriem@manorrock.com) */ public class HerringServletRequestListener implements ServletRequestListener { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(HerringServletRequestListener.class.getName()); /** * Constructor. */ public HerringServletRequestListener() { } @Override public void requestDestroyed(ServletRequestEvent event) { LOGGER.log(DEBUG, "Removing InitialContext"); ThreadInitialContextFactory.removeInitialContext(); } @Override public void requestInitialized(ServletRequestEvent event) { LOGGER.log(DEBUG, "Setting InitialContext"); WebApplication webApplication = (WebApplication) event.getServletContext(); Context context = (Context) webApplication.getAttribute(Context.class.getName()); ThreadInitialContextFactory.setInitialContext(context); } } ================================================ FILE: extension/herring/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module integrates Manorrock Herring into Piranha. * *

* The following property can be used to influence the workings of this module. *

*
" + name + "" + getServletConfig().getInitParameter(name) + "
* * * * * * * * * *
PropertyNotes
cloud.piranha.extension.herring.HerringExtension.enabledtrue to enable (default), false to disable
Configurable properties
* * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.herring { exports cloud.piranha.extension.herring; opens cloud.piranha.extension.herring; requires cloud.piranha.core.api; requires transitive com.manorrock.herring; requires transitive com.manorrock.herring.thread; requires jakarta.annotation; requires transitive java.naming; } ================================================ FILE: extension/herring/src/test/java/cloud/piranha/extension/herring/HerringExtensionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.herring; import static javax.naming.Context.INITIAL_CONTEXT_FACTORY; import static org.junit.jupiter.api.Assertions.assertEquals; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import org.junit.jupiter.api.Test; import cloud.piranha.embedded.EmbeddedPiranha; import cloud.piranha.embedded.EmbeddedPiranhaBuilder; import cloud.piranha.embedded.EmbeddedRequest; import cloud.piranha.embedded.EmbeddedResponse; import jakarta.servlet.ServletRequestEvent; import jakarta.servlet.ServletRequestListener; /** * The JUnit tests for the HerringExtension class. * * @author Manfred Riem (mriem@manorrock.com) */ class HerringExtensionTest { /** * Test configure method. * * @throws Exception when a serious error occurs. */ @Test void testConfigure() throws Exception { System.setProperty(INITIAL_CONTEXT_FACTORY, HerringInitialContextFactory.class.getName()); EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .extension(HerringExtension.class) .listener(TestServletRequestListener.class.getName()) .build() .start(); EmbeddedRequest request = new EmbeddedRequest(); EmbeddedResponse response = new EmbeddedResponse(); piranha.service(request, response); Context context1 = (Context) piranha.getWebApplication().getAttribute(Context.class.getName()); Context context2 = (Context) piranha.getWebApplication().getAttribute("InitialContext"); assertEquals( context1.getEnvironment().get("TheSame"), context2.getEnvironment().get("TheSame")); } /** * Test configure method. * * @throws Exception when a serious error occurs. */ @Test void testConfigure2() throws Exception { new EmbeddedPiranhaBuilder() .extension(HerringExtension.class) .listener(TestServletRequestListener.class.getName()) .build() .start(); assertEquals(System.getProperty(INITIAL_CONTEXT_FACTORY), HerringInitialContextFactory.class.getName()); } /** * Inner class used to test if the Context set by listener is equal to the * one set as the attribute on the WebApplication instance. */ public static class TestServletRequestListener implements ServletRequestListener { /** * Constructor. */ public TestServletRequestListener() { } @Override public void requestInitialized(ServletRequestEvent event) { try { InitialContext context = new InitialContext(); context.addToEnvironment("TheSame", true); event.getServletContext().setAttribute("InitialContext", context); } catch (NamingException ne) { ne.printStackTrace(); } } } } ================================================ FILE: extension/hibernate-validator/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-hibernate-validator jar Piranha - Extension - Hibernate Validator UTF-8 cloud.piranha.core piranha-core-api ${project.version} compile org.hibernate.validator hibernate-validator runtime org.hibernate.validator hibernate-validator-cdi runtime ================================================ FILE: extension/hibernate-validator/src/main/java/cloud/piranha/extension/hibernate/validator/HibernateValidatorExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.hibernate.validator; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import java.lang.System.Logger; import static java.lang.System.Logger.Level.TRACE; /** * The extension that delivers Hibernate Validator to Piranha. * * @author Manfred Riem (mriem@manorrock.com) */ public class HibernateValidatorExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(HibernateValidatorExtension.class.getName()); /** * Constructor. */ public HibernateValidatorExtension() { } /** * Configure the extension. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { LOGGER.log(TRACE, "Configuring Hibernate Validator extension"); } } ================================================ FILE: extension/hibernate-validator/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers integration of Hibernate Validator into Piranha. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.hibernate.validator { exports cloud.piranha.extension.hibernate.validator; opens cloud.piranha.extension.hibernate.validator; requires cloud.piranha.core.api; } ================================================ FILE: extension/jaxb/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-jaxb jar Piranha - Extension - JAXB cloud.piranha.core piranha-core-api ${project.version} compile cloud.piranha.extension piranha-extension-angus ${project.version} provided cloud.piranha.extension piranha-extension-scinitializer ${project.version} provided org.glassfish.jaxb jaxb-runtime ================================================ FILE: extension/jaxb/src/main/java/cloud/piranha/extension/jaxb/JAXBExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.jaxb; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import java.lang.System.Logger; import static java.lang.System.Logger.Level.TRACE; /** * The extension that delivers Eclipse JAXB to Piranha. * * @author Manfred Riem (mriem@manorrock.com) */ public class JAXBExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(JAXBExtension.class.getName()); /** * Constructor. */ public JAXBExtension() { } /** * Configure the extension. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { LOGGER.log(TRACE, "Configuring JAXB extension"); } } ================================================ FILE: extension/jaxb/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Eclipse JAXB integration extension. * *

* This extension integrates Eclipse JAXB into Piranha. *

* * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.jaxb { requires cloud.piranha.core.api; requires static cloud.piranha.extension.angus; requires static cloud.piranha.extension.scinitializer; } ================================================ FILE: extension/jersey/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-jersey jar Piranha - Extension - Jersey cloud.piranha.core piranha-core-api ${project.version} compile cloud.piranha.extension piranha-extension-scinitializer ${project.version} provided jakarta.annotation jakarta.annotation-api provided jakarta.inject jakarta.inject-api provided jakarta.enterprise jakarta.enterprise.cdi-api provided org.glassfish.jersey.containers jersey-container-servlet runtime org.glassfish.jersey.containers jersey-container-servlet-core runtime org.glassfish.jersey.core jersey-common runtime org.glassfish.jersey.ext.cdi jersey-cdi1x runtime org.glassfish.jersey.inject jersey-hk2 runtime org.glassfish.jersey.media jersey-media-json-binding runtime org.glassfish.jersey.media jersey-media-json-processing runtime org.glassfish.jersey.media jersey-media-sse runtime ================================================ FILE: extension/jersey/src/main/java/cloud/piranha/extension/jersey/JerseyExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.jersey; import jakarta.enterprise.event.Observes; import jakarta.enterprise.inject.spi.BeanManager; import jakarta.enterprise.inject.spi.BeforeBeanDiscovery; import jakarta.enterprise.inject.spi.Extension; import java.lang.System.Logger; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import static java.lang.System.Logger.Level.TRACE; /** * The extension that delivers Eclipse Jersey to Piranha. * * @author Manfred Riem (mriem@manorrock.com) */ public class JerseyExtension implements WebApplicationExtension, Extension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(JerseyExtension.class.getName()); /** * Constructor. */ public JerseyExtension() { } /** * Configure the extension. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { LOGGER.log(TRACE, "Configuring Jersey extension"); } /** * Register a source and target bean to force the adding of a class analyzer. * * @param beforeBeanDiscovery the BeforeBeanDiscovery. * @param beanManager the BeanManager. */ public void register(@Observes BeforeBeanDiscovery beforeBeanDiscovery, BeanManager beanManager) { LOGGER.log(TRACE, "Registering beans to force adding of class analyzer"); /* * Force a class analyzer to be added that makes sure a REST resource is * not attempted to be injected by both CDI and HK2. * * See https://github.com/eclipse-ee4j/jersey/issues/5745 on why this is * needed. */ addAnnotatedTypes(beforeBeanDiscovery, beanManager, JerseyTargetBean.class, JerseySourceBean.class); } /** * Add annotated types. * * @param beforeBeanDiscovery the BeforeBeanDiscovery. * @param beanManager the BeanManager. * @param types the types to add. */ public static void addAnnotatedTypes(BeforeBeanDiscovery beforeBeanDiscovery, BeanManager beanManager, Class... types) { LOGGER.log(TRACE, "Adding annotated types"); for (Class type : types) { beforeBeanDiscovery.addAnnotatedType( beanManager.createAnnotatedType(type), "JerseyExtension " + type.getName()); } } } ================================================ FILE: extension/jersey/src/main/java/cloud/piranha/extension/jersey/JerseySourceBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.jersey; import jakarta.enterprise.context.Dependent; /** * A simple source bean to force the addition of the class analyzer. * * @author Arjan Tijms */ @Dependent public class JerseySourceBean { /** * Constructor. */ public JerseySourceBean() { } } ================================================ FILE: extension/jersey/src/main/java/cloud/piranha/extension/jersey/JerseyTargetBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.jersey; import jakarta.enterprise.context.Dependent; import jakarta.inject.Inject; /** * A simple target bean to force the addition of the class analyzer. * * @author Arjan Tijms */ @Dependent public class JerseyTargetBean { /** * Store the source bean. */ @Inject JerseySourceBean bean; /** * Constructor. */ public JerseyTargetBean() { } } ================================================ FILE: extension/jersey/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Eclipse Jersey integration extension. * *

* This extension integrates Eclipse Jersey into Piranha. See * https://github.com/eclipse-ee4j/jersey for more information about its * project. *

* * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.jersey { requires cloud.piranha.core.api; requires static cloud.piranha.extension.scinitializer; requires static jakarta.cdi; } ================================================ FILE: extension/jersey/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension ================================================ cloud.piranha.extension.jersey.JerseyExtension ================================================ FILE: extension/jstl/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-jstl jar Piranha - Extension - JSTL cloud.piranha.core piranha-core-api ${project.version} compile cloud.piranha.extension piranha-extension-scinitializer ${project.version} provided org.glassfish.web jakarta.servlet.jsp.jstl runtime ================================================ FILE: extension/jstl/src/main/java/cloud/piranha/extension/jstl/JSTLExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.jstl; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import java.lang.System.Logger; import static java.lang.System.Logger.Level.TRACE; /** * The extension that delivers JSTL to Piranha. * * @author Manfred Riem (mriem@manorrock.com) */ public class JSTLExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(JSTLExtension.class.getName()); /** * Default constructor. */ public JSTLExtension() { } /** * Configure the extension. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { LOGGER.log(TRACE, "Configuring JSTL extension"); } } ================================================ FILE: extension/jstl/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module integrates JSTL into Piranha. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.jstl { requires cloud.piranha.core.api; requires static cloud.piranha.extension.scinitializer; } ================================================ FILE: extension/micro/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-micro Piranha - Extension - Micro This module delivers the default extension for Piranha Micro. cloud.piranha.core piranha-core-impl ${project.version} compile cloud.piranha.extension piranha-extension-fileupload ${project.version} compile cloud.piranha.extension piranha-extension-declared ${project.version} compile cloud.piranha.extension piranha-extension-datasource ${project.version} compile cloud.piranha.extension piranha-extension-angus ${project.version} compile cloud.piranha.extension piranha-extension-expressly ${project.version} compile cloud.piranha.extension piranha-extension-jaxb ${project.version} compile cloud.piranha.extension piranha-extension-jersey ${project.version} compile cloud.piranha.extension piranha-extension-jstl ${project.version} compile cloud.piranha.extension piranha-extension-mojarra ${project.version} compile cloud.piranha.extension piranha-extension-parsson ${project.version} compile cloud.piranha.extension piranha-extension-soteria ${project.version} compile cloud.piranha.extension piranha-extension-tyrus ${project.version} compile cloud.piranha.extension piranha-extension-wasp ${project.version} compile cloud.piranha.extension piranha-extension-yasson ${project.version} compile cloud.piranha.extension piranha-extension-eclipselink ${project.version} compile cloud.piranha.extension piranha-extension-naming-cdi ${project.version} compile cloud.piranha.extension piranha-extension-omnifaces-config ${project.version} compile cloud.piranha.extension piranha-extension-omnifaces-microprofile-jwt-auth ${project.version} compile cloud.piranha.extension piranha-extension-omnifaces-omniservices ${project.version} compile cloud.piranha.extension piranha-extension-omnifaces-omniutils ${project.version} compile cloud.piranha.extension piranha-extension-omnifish-omnibeans ${project.version} compile cloud.piranha.extension piranha-extension-omnifish-transact ${project.version} compile cloud.piranha.extension piranha-extension-policy ${project.version} cloud.piranha.extension piranha-extension-hibernate-validator ${project.version} runtime cloud.piranha.extension piranha-extension-weld ${project.version} runtime cloud.piranha.extension piranha-extension-scinitializer ${project.version} compile cloud.piranha.extension piranha-extension-security-jakarta ${project.version} cloud.piranha.extension piranha-extension-servletannotations ${project.version} compile cloud.piranha.extension piranha-extension-tempdir ${project.version} compile cloud.piranha.extension piranha-extension-webxml ${project.version} compile cloud.piranha.extension piranha-extension-welcomefile ${project.version} compile jakarta.ejb jakarta.ejb-api runtime org.eclipse.microprofile.config microprofile-config-api runtime ================================================ FILE: extension/micro/src/main/java/cloud/piranha/extension/micro/MicroExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.micro; import static java.util.Arrays.asList; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import cloud.piranha.core.api.WebApplicationExtensionContext; import cloud.piranha.extension.declared.DeclaredExtension; import cloud.piranha.extension.datasource.DefaultDatasourceExtension; import cloud.piranha.extension.eclipselink.EclipseLinkExtension; import cloud.piranha.extension.exousia.AuthorizationPostInitializer; import cloud.piranha.extension.fileupload.FileUploadExtension; import cloud.piranha.extension.naming.cdi.NamingExtension; import cloud.piranha.extension.policy.PolicyExtension; import cloud.piranha.extension.scinitializer.ServletContainerInitializerExtension; import cloud.piranha.extension.security.jakarta.JakartaSecurityExtension; import cloud.piranha.extension.security.servlet.ServletSecurityManagerExtension; import cloud.piranha.extension.servletannotations.ServletAnnotationsExtension; import cloud.piranha.extension.tempdir.TempDirExtension; import cloud.piranha.extension.transact.TransactExtension; import cloud.piranha.extension.wasp.WaspExtension; import cloud.piranha.extension.webxml.WebXmlExtension; import cloud.piranha.extension.welcomefile.WelcomeFileExtension; /** * The {@link WebApplicationExtension} used to configure a web application for * Piranha Micro. * * @author Manfred Riem (mriem@manorrock.com) */ public class MicroExtension implements WebApplicationExtension { /** * Constructor. */ public MicroExtension() { } @Override public void extend(WebApplicationExtensionContext context) { // Servlet context.add(PolicyExtension.class); // Policy context.add(TempDirExtension.class); // TEMPDIR context.add(WelcomeFileExtension.class); // welcome-file context.add(ServletSecurityManagerExtension.class); // Servlet security context.add(FileUploadExtension.class); // Upload context.add(WebXmlExtension.class); // web.xml context.add(ServletAnnotationsExtension.class); // Servlet annotations context.add(DeclaredExtension.class); // context.add(NamingExtension.class); // Naming (JNDI) context.add(DefaultDatasourceExtension.class); // Default data source context.add(JakartaSecurityExtension.class); // Jakarta Security (includes Jakarta CDI) context.add(TransactExtension.class); // Jakarta Transaction context.add(WaspExtension.class); // Jakarta Pages context.add(EclipseLinkExtension.class); // Jakarta Persistence } @Override public void configure(WebApplication webApplication) { new ServletContainerInitializerExtension( true, asList("org.glassfish.soteria.servlet.SamRegistrationInstaller")) .configure(webApplication); webApplication.addInitializer(new AuthorizationPostInitializer()); } } ================================================ FILE: extension/micro/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ import cloud.piranha.core.api.WebApplicationExtension; import cloud.piranha.extension.micro.MicroExtension; /** * This module delivers the meta extension for Piranha Micro. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.micro { exports cloud.piranha.extension.micro; opens cloud.piranha.extension.micro; provides WebApplicationExtension with MicroExtension; requires cloud.piranha.core.api; requires cloud.piranha.extension.fileupload; requires cloud.piranha.extension.declared; requires cloud.piranha.extension.datasource; requires cloud.piranha.extension.eclipselink; requires cloud.piranha.extension.exousia; requires cloud.piranha.extension.expressly; requires cloud.piranha.extension.jersey; requires cloud.piranha.extension.naming.cdi; requires cloud.piranha.extension.policy; requires cloud.piranha.extension.scinitializer; requires cloud.piranha.extension.security.jakarta; requires cloud.piranha.extension.security.servlet; requires cloud.piranha.extension.servletannotations; requires cloud.piranha.extension.tempdir; requires cloud.piranha.extension.transact; requires cloud.piranha.extension.wasp; requires cloud.piranha.extension.webxml; requires cloud.piranha.extension.welcomefile; } ================================================ FILE: extension/micro/src/main/resources/META-INF/services/cloud.piranha.core.api.WebApplicationExtension ================================================ cloud.piranha.extension.micro.MicroExtension ================================================ FILE: extension/microprofile/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-microprofile jar Piranha - Extension - MicroProfile This module delivers the extensions for MicroProfile. cloud.piranha.core piranha-core-api ${project.version} compile cloud.piranha.extension piranha-extension-annotationscan ${project.version} compile cloud.piranha.extension piranha-extension-annotationscan-classfile ${project.version} compile cloud.piranha.extension piranha-extension-herring ${project.version} compile cloud.piranha.extension piranha-extension-jersey ${project.version} compile cloud.piranha.extension piranha-extension-yasson ${project.version} compile cloud.piranha.extension piranha-extension-weld ${project.version} compile cloud.piranha.extension piranha-extension-scinitializer ${project.version} compile ================================================ FILE: extension/microprofile/src/main/java/cloud/piranha/extension/microprofile/MicroProfileExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.microprofile; import cloud.piranha.core.api.WebApplicationExtension; import cloud.piranha.core.api.WebApplicationExtensionContext; import cloud.piranha.extension.annotationscan.AnnotationScanExtension; import cloud.piranha.extension.annotationscan.classfile.ClassfileAnnotationScanExtension; import cloud.piranha.extension.herring.HerringExtension; import cloud.piranha.extension.scinitializer.ServletContainerInitializerExtension; /** * The extension that delivers the extensions for MicroProfile. * * @author Manfred Riem (mriem@manorrock.com) */ public class MicroProfileExtension implements WebApplicationExtension { /** * Constructor. */ public MicroProfileExtension() { } @Override public void extend(WebApplicationExtensionContext context) { context.add(HerringExtension.class); // Herring (JNDI) context.add(getAnnotationScanExtensionClass()); context.add(ServletContainerInitializerExtension.class); // ServletContainerInitializer } private static Class getAnnotationScanExtensionClass() { if (System.getProperty(ClassfileAnnotationScanExtension.EXPERIMENTAL_PROPERTY) != null) { return ClassfileAnnotationScanExtension.class; // Annotation scanning using the new Classfile API } return AnnotationScanExtension.class; // Annotation scanning } } ================================================ FILE: extension/microprofile/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the meta extension for MicroProfile. * *

* The following extensions and/or dependencies are delivered as part of this * meta extension: *

*
    *
  • Annotation Scanning
  • *
  • Eclipse Parsson (JSON)
  • *
  • Eclipse Yasson (JSON-B)
  • *
  • Eclipse Jersey (REST)
  • *
  • Naming (JNDI)
  • *
  • ServletContainerInitializer
  • *
*/ module cloud.piranha.extension.microprofile { exports cloud.piranha.extension.microprofile; opens cloud.piranha.extension.microprofile; requires cloud.piranha.core.api; requires cloud.piranha.extension.annotationscan; requires cloud.piranha.extension.annotationscan.classfile; requires cloud.piranha.extension.herring; requires cloud.piranha.extension.jersey; requires cloud.piranha.extension.scinitializer; requires cloud.piranha.extension.yasson; } ================================================ FILE: extension/mojarra/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-mojarra jar Piranha - Extension - Mojarra cloud.piranha.core piranha-core-api ${project.version} compile cloud.piranha.extension piranha-extension-scinitializer ${project.version} provided org.glassfish jakarta.faces runtime ================================================ FILE: extension/mojarra/src/main/java/cloud/piranha/extension/mojarra/MojarraExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.mojarra; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import java.lang.System.Logger; import static java.lang.System.Logger.Level.TRACE; /** * The extension that delivers Mojarra to Piranha. * * @author Manfred Riem (mriem@manorrock.com) */ public class MojarraExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(MojarraExtension.class.getName()); /** * Constructor. */ public MojarraExtension() { } /** * Configure the extension. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { LOGGER.log(TRACE, "Configuring Mojarra extension"); } } ================================================ FILE: extension/mojarra/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Mojarra integration extension. * *

* This extension integrates Mojarra into Piranha. See * https://github.com/eclipse-ee4j/mojarra for more information about its * project. *

* * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.mojarra { requires cloud.piranha.core.api; requires static cloud.piranha.extension.scinitializer; } ================================================ FILE: extension/naming-cdi/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-naming-cdi jar Piranha - Extension - Naming CDI cloud.piranha.core piranha-core-api ${project.version} compile com.manorrock.herring herring compile com.manorrock.herring herring-thread compile jakarta.annotation jakarta.annotation-api compile jakarta.enterprise jakarta.enterprise.cdi-api cloud.piranha piranha-embedded ${project.version} test ================================================ FILE: extension/naming-cdi/src/main/java/cloud/piranha/extension/naming/cdi/DefaultInitialContextFactory.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.naming.cdi; import java.util.Hashtable; import javax.naming.Context; import javax.naming.NamingException; import javax.naming.spi.InitialContextFactory; import com.manorrock.herring.DefaultInitialContext; /** * The default InitialContextFactory. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultInitialContextFactory implements InitialContextFactory { /** * Stores the initial context. */ private static Context theOneAndOnlyInstance = new DefaultInitialContext(); /** * Constructor. */ public DefaultInitialContextFactory() { } /** * Sets the static (initial) context * @param context the (initial) context */ public static void setInitialContext(Context context) { theOneAndOnlyInstance = context; } /** * Get the initial context. * * @return the initial context. * @param environment the environment. * @throws NamingException when a naming error occurs. */ @Override public Context getInitialContext(Hashtable environment) throws NamingException { return theOneAndOnlyInstance; } } ================================================ FILE: extension/naming-cdi/src/main/java/cloud/piranha/extension/naming/cdi/NamingExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.naming.cdi; import static java.lang.System.Logger.Level.DEBUG; import static java.lang.System.Logger.Level.INFO; import static java.lang.System.Logger.Level.WARNING; import static javax.naming.Context.INITIAL_CONTEXT_FACTORY; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Optional; import javax.naming.CompositeName; import javax.naming.Context; import javax.naming.NamingException; import javax.naming.Reference; import javax.naming.spi.NamingManager; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import com.manorrock.herring.DefaultInitialContext; import jakarta.annotation.Resource; import jakarta.enterprise.inject.spi.CDI; /** * The WebApplicationExtension that is responsible for setting up the proper * Context instance so it can be made available during web application * initialization and subsequently during request processing as well as * delivering listeners to set/remove the Context from the current thread. * * @author Manfred Riem (mriem@manorrock.com) */ public class NamingExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(NamingExtension.class.getName()); /** * Constructor. */ public NamingExtension() { } /** * Configure the web application. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { LOGGER.log(DEBUG, "Configuring JNDI support"); if (System.getProperty(INITIAL_CONTEXT_FACTORY) == null) { LOGGER.log(INFO, INITIAL_CONTEXT_FACTORY + " was not set, setting it"); System.setProperty(INITIAL_CONTEXT_FACTORY, DefaultInitialContextFactory.class.getName()); } if (!System.getProperty(INITIAL_CONTEXT_FACTORY).equals(DefaultInitialContextFactory.class.getName())) { LOGGER.log(WARNING, INITIAL_CONTEXT_FACTORY + " is not set to " + DefaultInitialContextFactory.class.getName()); } Context context = new DefaultInitialContext(); Context proxyContext = (Context) Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(), new Class[] { Context.class }, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { Object returnValue = null; try { returnValue = method.invoke(context, args); } catch (InvocationTargetException e) { boolean invoked = false; try { if (method.getName().equals("lookup") && e.getCause() instanceof NamingException) { String jndiName = args[0].toString(); if (jndiName.startsWith("java:comp/env/")) { String classNameWithField = jndiName.substring("java:comp/env/".length()); String[] classNameAndField = classNameWithField.split("/"); if (classNameAndField.length == 2) { String className = classNameAndField[0]; String fieldName = classNameAndField[1]; Class beanClass = Class.forName(className, false, Thread.currentThread().getContextClassLoader()); Resource[] resources = null; Class type = null; try { Field beanField = beanClass.getDeclaredField(fieldName); resources = beanField.getAnnotationsByType(Resource.class); type = beanField.getType(); } catch (NoSuchFieldException | SecurityException exception) { char[] chars = fieldName.toCharArray(); chars[0] = Character.toUpperCase(chars[0]); String methodName = "set" + new String(chars); Optional optionalMethod = Arrays.stream(beanClass.getDeclaredMethods()) .filter(m -> m.getName().equals(methodName)) .filter(m -> m.getParameterCount() == 1) .filter(m -> m.getAnnotationsByType(Resource.class) != null) .findFirst(); // ignore overloaded for now if (optionalMethod.isPresent()) { resources = optionalMethod.get().getAnnotationsByType(Resource.class); type = optionalMethod.get().getParameterTypes()[0]; } } if (resources != null && resources.length > 0) { Resource resourceAnnnotation = resources[0]; String lookup = resourceAnnnotation.lookup(); if (!"".equals(lookup)) { returnValue = method.invoke(context, new Object[] {lookup}); args = new Object[] {lookup}; invoked = true; } else { return CDI.current().select(type).get(); } } } } else if (jndiName.startsWith("ejblocal:")) { String typeName = jndiName.substring("ejblocal:".length()); return CDI.current() .select( Class.forName( typeName, false, Thread.currentThread().getContextClassLoader()) ) .get(); } } } catch (Throwable t) { if (t instanceof InvocationTargetException invocationException && invocationException.getTargetException() instanceof NamingException namingException) { throw namingException; } e.addSuppressed(t); } if (!invoked) { throw e; } } // De-referencing can eventually be moved to DefaultInitialContext if (method.getName().equals("lookup") && returnValue instanceof Reference) { returnValue = NamingManager.getObjectInstance( returnValue, new CompositeName(args[0].toString()), null, null); } return returnValue; } catch (InvocationTargetException e) { throw e.getCause(); } } }); DefaultInitialContextFactory.setInitialContext(proxyContext); webApplication.setAttribute(Context.class.getName(), proxyContext); } } ================================================ FILE: extension/naming-cdi/src/main/java/cloud/piranha/extension/naming/cdi/NamingServletRequestListener.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.naming.cdi; import cloud.piranha.core.api.WebApplication; import com.manorrock.herring.thread.ThreadInitialContextFactory; import jakarta.servlet.ServletRequestEvent; import jakarta.servlet.ServletRequestListener; import static java.lang.System.Logger.Level.DEBUG; import javax.naming.Context; /** * The ServletRequestListener that sets the Context instance on the current * thread just before request processing and removes the Context instance from * the current after the request has been processed. * * @author Manfred Riem (mriem@manorrock.com) */ public class NamingServletRequestListener implements ServletRequestListener { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(NamingServletRequestListener.class.getName()); /** * Constructor. */ public NamingServletRequestListener() { } @Override public void requestDestroyed(ServletRequestEvent event) { LOGGER.log(DEBUG, "Removing InitialContext"); ThreadInitialContextFactory.removeInitialContext(); } @Override public void requestInitialized(ServletRequestEvent event) { LOGGER.log(DEBUG, "Setting InitialContext"); WebApplication webApplication = (WebApplication) event.getServletContext(); Context context = (Context) webApplication.getAttribute(Context.class.getName()); ThreadInitialContextFactory.setInitialContext(context); } } ================================================ FILE: extension/naming-cdi/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the CDI variant JNDI integration extension. * * @author Arjan Tijms * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.naming.cdi { exports cloud.piranha.extension.naming.cdi; opens cloud.piranha.extension.naming.cdi; requires cloud.piranha.core.api; requires transitive com.manorrock.herring; requires transitive com.manorrock.herring.thread; requires jakarta.annotation; requires jakarta.cdi; requires transitive java.naming; } ================================================ FILE: extension/naming-cdi/src/test/java/cloud/piranha/extension/naming/NamingExtensionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.naming; import static javax.naming.Context.INITIAL_CONTEXT_FACTORY; import static org.junit.jupiter.api.Assertions.assertEquals; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import org.junit.jupiter.api.Test; import cloud.piranha.embedded.EmbeddedPiranha; import cloud.piranha.embedded.EmbeddedPiranhaBuilder; import cloud.piranha.embedded.EmbeddedRequest; import cloud.piranha.embedded.EmbeddedResponse; import cloud.piranha.extension.naming.cdi.DefaultInitialContextFactory; import cloud.piranha.extension.naming.cdi.NamingExtension; import jakarta.servlet.ServletRequestEvent; import jakarta.servlet.ServletRequestListener; /** * The JUnit tests for the NamingExtension class. * * @author Manfred Riem (mriem@manorrock.com) */ class NamingExtensionTest { /** * Test configure method. * * @throws Exception when a serious error occurs. */ @Test void testConfigure() throws Exception { System.setProperty(INITIAL_CONTEXT_FACTORY, DefaultInitialContextFactory.class.getName()); EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .extension(NamingExtension.class) .listener(TestServletRequestListener.class.getName()) .build() .start(); EmbeddedRequest request = new EmbeddedRequest(); EmbeddedResponse response = new EmbeddedResponse(); piranha.service(request, response); Context context1 = (Context) piranha.getWebApplication().getAttribute(Context.class.getName()); Context context2 = (Context) piranha.getWebApplication().getAttribute("InitialContext"); assertEquals( context1.getEnvironment().get("TheSame"), context2.getEnvironment().get("TheSame")); } /** * Test configure method. * * @throws Exception when a serious error occurs. */ @Test void testConfigure2() throws Exception { new EmbeddedPiranhaBuilder() .extension(NamingExtension.class) .listener(TestServletRequestListener.class.getName()) .build() .start(); assertEquals(System.getProperty(INITIAL_CONTEXT_FACTORY), DefaultInitialContextFactory.class.getName()); } /** * Inner class used to test if the Context set by listener is equal to the * one set as the attribute on the WebApplication instance. */ public static class TestServletRequestListener implements ServletRequestListener { /** * Constructor. */ public TestServletRequestListener() { } @Override public void requestInitialized(ServletRequestEvent event) { try { InitialContext context = new InitialContext(); context.addToEnvironment("TheSame", true); event.getServletContext().setAttribute("InitialContext", context); } catch (NamingException ne) { ne.printStackTrace(); } } } } ================================================ FILE: extension/omnifaces-config/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-omnifaces-config jar Piranha - Extension - OmniFaces Config cloud.piranha.core piranha-core-impl ${project.version} compile org.omnifaces omni-mp-config compile ================================================ FILE: extension/omnifaces-config/src/main/java/cloud/piranha/extension/omnifaces/config/ConfigExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.omnifaces.config; import java.lang.System.Logger; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import static java.lang.System.Logger.Level.TRACE; /** * The extension that delivers OmniFaces Config to Piranha. * * @author Arjan Tijms */ public class ConfigExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(ConfigExtension.class.getName()); /** * Constructor. */ public ConfigExtension() { } /** * Configure the web application. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { LOGGER.log(TRACE, "Configuring OmniFaces Config extension"); } } ================================================ FILE: extension/omnifaces-config/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the OmniFaces Config integration extension. * * @author Arjan Tijms */ module cloud.piranha.extension.omnifaces.config { exports cloud.piranha.extension.omnifaces.config; opens cloud.piranha.extension.omnifaces.config; requires cloud.piranha.core.api; } ================================================ FILE: extension/omnifaces-microprofile-jwt-auth/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-omnifaces-microprofile-jwt-auth jar Piranha - Extension - OmniFaces Micro Profile JWT Auth cloud.piranha.core piranha-core-impl ${project.version} compile org.omnifaces microprofile-jwt-auth compile ================================================ FILE: extension/omnifaces-microprofile-jwt-auth/src/main/java/cloud/piranha/extension/omnifaces/microprofile/jwt/auth/MicroProfileJWTAuthExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.omnifaces.microprofile.jwt.auth; import java.lang.System.Logger; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import static java.lang.System.Logger.Level.TRACE; /** * The extension that delivers OmniFaces Micro Profile JWT Auth to Piranha. * * @author Arjan Tijms */ public class MicroProfileJWTAuthExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(MicroProfileJWTAuthExtension.class.getName()); /** * Constructor. */ public MicroProfileJWTAuthExtension() { } /** * Configure the web application. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { LOGGER.log(TRACE, "Configuring OmniFaces Micro Profile JWT Auth extension"); } } ================================================ FILE: extension/omnifaces-microprofile-jwt-auth/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the OmniFaces Micro Profile JWT Auth integration extension. * * @author Arjan Tijms */ module cloud.piranha.extension.omnifaces.microprofile.jwt.auth { exports cloud.piranha.extension.omnifaces.microprofile.jwt.auth; opens cloud.piranha.extension.omnifaces.microprofile.jwt.auth; requires cloud.piranha.core.api; } ================================================ FILE: extension/omnifaces-omniservices/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-omnifaces-omniservices jar Piranha - Extension - OmniFaces OmniServices cloud.piranha.core piranha-core-impl ${project.version} compile org.omnifaces omniservices compile ================================================ FILE: extension/omnifaces-omniservices/src/main/java/cloud/piranha/extension/omniservices/OmniServicesExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.omniservices; import java.lang.System.Logger; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import static java.lang.System.Logger.Level.TRACE; /** * The extension that delivers OmniServices to Piranha. * * @author Arjan Tijms */ public class OmniServicesExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(OmniServicesExtension.class.getName()); /** * Constructor. */ public OmniServicesExtension() { } /** * Configure the web application. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { LOGGER.log(TRACE, "Configuring OmniServices extension"); } } ================================================ FILE: extension/omnifaces-omniservices/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the OmniFaces OmniServices integration extension. * * @author Arjan Tijms */ module cloud.piranha.extension.omniservices { exports cloud.piranha.extension.omniservices; opens cloud.piranha.extension.omniservices; requires cloud.piranha.core.api; } ================================================ FILE: extension/omnifaces-omniutils/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-omnifaces-omniutils jar Piranha - Extension - OmniFaces OmniUtils cloud.piranha.core piranha-core-impl ${project.version} compile org.omnifaces omniutils compile ================================================ FILE: extension/omnifaces-omniutils/src/main/java/cloud/piranha/extension/omniutils/OmniUtilsExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.omniutils; import java.lang.System.Logger; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import static java.lang.System.Logger.Level.TRACE; /** * The extension that delivers OmniUtils to Piranha. * * @author Arjan Tijms */ public class OmniUtilsExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(OmniUtilsExtension.class.getName()); /** * Constructor. */ public OmniUtilsExtension() { } /** * Configure the web application. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { LOGGER.log(TRACE, "Configuring OmniUtils extension"); } } ================================================ FILE: extension/omnifaces-omniutils/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the OmniFaces OmniUtils integration extension. * * @author Arjan Tijms */ module cloud.piranha.extension.omniutils { exports cloud.piranha.extension.omniutils; opens cloud.piranha.extension.omniutils; requires cloud.piranha.core.api; } ================================================ FILE: extension/omnifish-omnibeans/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-omnifish-omnibeans jar Piranha - Extension - OmniFish OmniBeans cloud.piranha.core piranha-core-impl ${project.version} compile ee.omnifish omnibeans compile ================================================ FILE: extension/omnifish-omnibeans/src/main/java/cloud/piranha/extension/omnibeans/OmniBeansExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.omnibeans; import java.lang.System.Logger; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import static java.lang.System.Logger.Level.TRACE; /** * The extension that delivers OmniBeans to Piranha. * * @author Arjan Tijms */ public class OmniBeansExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(OmniBeansExtension.class.getName()); /** * Constructor. */ public OmniBeansExtension() { } /** * Configure the web application. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { LOGGER.log(TRACE, "Configuring OmniBeans extension"); } } ================================================ FILE: extension/omnifish-omnibeans/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the OmniFish OmniBeans integration extension. * * @author Arjan Tijms */ module cloud.piranha.extension.omnibeans { exports cloud.piranha.extension.omnibeans; opens cloud.piranha.extension.omnibeans; requires cloud.piranha.core.api; } ================================================ FILE: extension/omnifish-transact/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-omnifish-transact jar Piranha - Extension - OmniFish Transact cloud.piranha.core piranha-core-impl ${project.version} compile ee.omnifish transact-cdi-beans compile jakarta.el jakarta.el-api provided jakarta.servlet jakarta.servlet-api provided cloud.piranha piranha-embedded ${project.version} test org.junit.jupiter junit-jupiter-api test org.junit.platform junit-platform-launcher test ================================================ FILE: extension/omnifish-transact/src/main/java/cloud/piranha/extension/transact/TransactExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.transact; import static java.lang.System.Logger.Level.WARNING; import java.lang.System.Logger; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import jakarta.servlet.ServletContainerInitializer; /** * The extension that will enable Transact integration * * @author Arjan Tijms */ public class TransactExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(TransactExtension.class.getName()); /** * Constructor. */ public TransactExtension() { } /** * Configure the web application. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { try { webApplication.addInitializer( webApplication.getClassLoader() .loadClass(TransactInitializer.class.getName()) .asSubclass(ServletContainerInitializer.class) .getDeclaredConstructor() .newInstance()); } catch (ReflectiveOperationException | SecurityException ex) { LOGGER.log(WARNING, "Unable to enable the Transact extension", ex); } } } ================================================ FILE: extension/omnifish-transact/src/main/java/cloud/piranha/extension/transact/TransactFilter.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.transact; import static ee.omnifish.transact.api.ComponentInvocation.ComponentInvocationType.SERVLET_INVOCATION; import java.io.IOException; import ee.omnifish.transact.api.ComponentInvocation; import ee.omnifish.transact.api.Globals; import ee.omnifish.transact.api.InvocationManager; import ee.omnifish.transact.api.impl.ComponentInvocationImpl; import ee.omnifish.transact.api.spi.ServiceLocator; import jakarta.inject.Inject; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpFilter; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.transaction.SystemException; import jakarta.transaction.TransactionManager; /** * The Transact filter. * * @author Arjan Tijms */ public class TransactFilter extends HttpFilter { private static final long serialVersionUID = 1L; /** * Injects ServiceLocator */ @Inject private ServiceLocator serviceLocator; /** * Injects InvocationManager */ @Inject private InvocationManager invocationManager; /** * Injects TransactionManager */ @Inject private TransactionManager transactionManager; /** * Constructor. */ public TransactFilter() { } @Override public void init() throws ServletException { Globals.setDefaultServiceLocator(serviceLocator); } @Override protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException { ComponentInvocation componentInvocation = new ComponentInvocationImpl(this, SERVLET_INVOCATION); try { invocationManager.preInvoke(componentInvocation); super.doFilter(req, res, chain); } finally { invocationManager.postInvoke(componentInvocation); if (transactionManager != null) { try { if (transactionManager.getTransaction() != null) { transactionManager.rollback(); } } catch (IllegalStateException | SecurityException | SystemException e) { throw new IllegalStateException(e); } } } } } ================================================ FILE: extension/omnifish-transact/src/main/java/cloud/piranha/extension/transact/TransactInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.transact; import static java.lang.System.Logger.Level.DEBUG; import java.lang.System.Logger; import java.util.Set; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.naming.Reference; import cloud.piranha.core.api.WebApplication; import ee.omnifish.transact.cdi.beans.JndiToCdiReference; import jakarta.enterprise.inject.spi.CDI; import jakarta.servlet.FilterRegistration; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; /** * The Transact initializer. * * @author Arjan Tijms */ public class TransactInitializer implements ServletContainerInitializer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(TransactInitializer.class.getName()); /** * Constructor. */ public TransactInitializer() { } /** * Initialize Transact. * * @param classes the classes. * @param servletContext the Servlet context. * @throws ServletException when a Servlet error occurs. */ @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { WebApplication application = (WebApplication) servletContext; LOGGER.log(DEBUG, "Initializing Transact"); if (!isCDIEnabled()) { return; } Reference reference = new JndiToCdiReference(); try { new InitialContext().bind("java:comp/UserTransaction", reference); new InitialContext().bind("java:appserver/TransactionManager", reference); new InitialContext().bind("java:comp/TransactionSynchronizationRegistry", reference); } catch (NamingException e) { throw new ServletException(e); } FilterRegistration.Dynamic dynamic = application.addFilter(TransactFilter.class.getSimpleName(), TransactFilter.class); dynamic.setAsyncSupported(true); application.addFilterMapping(TransactFilter.class.getSimpleName(), false, "/*"); System.setProperty("ee.omnifish.transact.jts.dblogging.use.nontx.connection.for.add", "true"); LOGGER.log(DEBUG, "Initialized Transact"); } private boolean isCDIEnabled() { try { CDI.current(); return true; } catch (IllegalStateException e) { return false; } } } ================================================ FILE: extension/omnifish-transact/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the OmniFish Transact integration extension. * *

* This extension integrates Transact into Piranha. See * https://github.com/OmniFish-EE/omni-transact for more information about its * project. *

* * @author Arjan Tijms */ module cloud.piranha.extension.transact { exports cloud.piranha.extension.transact; opens cloud.piranha.extension.transact; requires transitive cloud.piranha.core.api; requires cloud.piranha.core.impl; requires ee.omnifish.transact.api; requires ee.omnifish.transact.cdi.beans; requires transitive jakarta.servlet; requires java.naming; } ================================================ FILE: extension/parsson/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-parsson jar Piranha - Extension - Parsson cloud.piranha.core piranha-core-api ${project.version} compile cloud.piranha.extension piranha-extension-scinitializer ${project.version} provided org.eclipse.parsson parsson runtime ================================================ FILE: extension/parsson/src/main/java/cloud/piranha/extension/parsson/ParssonExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.parsson; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import java.lang.System.Logger; import static java.lang.System.Logger.Level.TRACE; /** * The extension that delivers Eclipse Parsson to Piranha. * * @author Manfred Riem (mriem@manorrock.com) */ public class ParssonExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(ParssonExtension.class.getName()); /** * Constructor. */ public ParssonExtension() { } /** * Configure the extension. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { LOGGER.log(TRACE, "Configuring Eclipse Parsson extension"); } } ================================================ FILE: extension/parsson/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Eclipse Parsson integration extension. * *

* This extension integrates Eclipse Parsson into Piranha. *

* * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.parsson { requires cloud.piranha.core.api; requires static cloud.piranha.extension.scinitializer; } ================================================ FILE: extension/platform/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-platform jar Piranha - Extension - Platform This module delivers the extensions for the Jakarta Full Profile (aka the Jakarta EE platform). cloud.piranha.core piranha-core-api ${project.version} compile cloud.piranha.extension piranha-extension-annotationscan ${project.version} compile cloud.piranha.extension piranha-extension-annotationscan-classfile ${project.version} compile cloud.piranha.extension piranha-extension-concurro ${project.version} compile cloud.piranha.extension piranha-extension-expressly ${project.version} compile cloud.piranha.extension piranha-extension-herring ${project.version} compile cloud.piranha.extension piranha-extension-jersey ${project.version} compile cloud.piranha.extension piranha-extension-yasson ${project.version} compile cloud.piranha.extension piranha-extension-weld ${project.version} compile cloud.piranha.extension piranha-extension-scinitializer ${project.version} compile ================================================ FILE: extension/platform/src/main/java/cloud/piranha/extension/platform/PlatformExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.platform; import cloud.piranha.core.api.WebApplicationExtension; import cloud.piranha.core.api.WebApplicationExtensionContext; import cloud.piranha.extension.annotationscan.AnnotationScanExtension; import cloud.piranha.extension.annotationscan.classfile.ClassfileAnnotationScanExtension; import cloud.piranha.extension.herring.HerringExtension; import cloud.piranha.extension.scinitializer.ServletContainerInitializerExtension; /** * The extension that delivers the extensions for Jakarta EE platform. * * @author Manfred Riem (mriem@manorrock.com) */ public class PlatformExtension implements WebApplicationExtension { /** * Constructor. */ public PlatformExtension() { } @Override public void extend(WebApplicationExtensionContext context) { context.add(HerringExtension.class); // Herring (JNDI) context.add(getAnnotationScanExtensionClass()); // Annotation scanning context.add(ServletContainerInitializerExtension.class); // ServletContainerInitializer } private static Class getAnnotationScanExtensionClass() { if (System.getProperty(ClassfileAnnotationScanExtension.EXPERIMENTAL_PROPERTY) != null) { return ClassfileAnnotationScanExtension.class; // Annotation scanning using the new Classfile API } return AnnotationScanExtension.class; // Annotation scanning } } ================================================ FILE: extension/platform/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the meta extension for the Jakarta EE platform. * *

* The following extensions and/or dependencies are delivered as part of this * meta extension: *

*
    *
  • Annotation Scanning
  • *
  • Eclipse Expressly (EL)
  • *
  • Eclipse Parsson (JSON)
  • *
  • Eclipse Yasson (JSON-B)
  • *
  • Glassfish Jersey (REST)
  • *
  • Herring (JNDI)
  • *
  • ServletContainerInitializer
  • *
*/ module cloud.piranha.extension.platform { exports cloud.piranha.extension.platform; opens cloud.piranha.extension.platform; requires transitive cloud.piranha.core.api; requires cloud.piranha.extension.annotationscan; requires cloud.piranha.extension.annotationscan.classfile; requires cloud.piranha.extension.expressly; requires cloud.piranha.extension.herring; requires cloud.piranha.extension.jersey; requires cloud.piranha.extension.scinitializer; requires cloud.piranha.extension.yasson; } ================================================ FILE: extension/policy/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-policy jar Piranha - Extension - Policy cloud.piranha.core piranha-core-api ${project.version} compile cloud.piranha piranha-embedded ${project.version} test org.junit.jupiter junit-jupiter-api test 5.10.3 org.junit.jupiter junit-jupiter-params test 5.10.3 org.junit.jupiter junit-jupiter-engine test 5.10.3 org.apache.maven.plugins maven-surefire-plugin @{argLine} --add-opens cloud.piranha.extension.policy/cloud.piranha.extension.policy.internal=ALL-UNNAMED org.jacoco jacoco-maven-plugin default-check check BUNDLE INSTRUCTION COVEREDRATIO 0.80 CLASS MISSEDCOUNT 0 ================================================ FILE: extension/policy/src/main/java/cloud/piranha/extension/policy/PolicyExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.policy; import cloud.piranha.extension.policy.internal.InternalPolicyThreadLocal; import cloud.piranha.extension.policy.internal.InternalPolicyServletContextListener; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import cloud.piranha.extension.policy.internal.InternalPolicyServletRequestListener; import static java.lang.System.Logger.Level.TRACE; import static java.lang.System.Logger.Level.WARNING; import java.security.NoSuchAlgorithmException; import java.security.Policy; /** * The WebApplicationExtension that is responsible for setting up the proper * Policy instance so it can be made available during web application * initialization and subsequently during request processing as well as * delivering listeners to set/remove the Policy from the current thread. * * @author Manfred Riem (mriem@manorrock.com) */ public class PolicyExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(PolicyExtension.class.getName()); /** * Constructor. */ public PolicyExtension() { } /** * Configure the web application. * * @param webApplication the web application. */ @SuppressWarnings("removal") @Override public void configure(WebApplication webApplication) { Boolean disabled = Boolean.valueOf((String) webApplication.getAttribute(PolicyExtension.class.getName() + ".disable")); if (!disabled) { try { LOGGER.log(TRACE, "Configuring Policy extension"); Policy policy = Policy.getInstance("JavaPolicy", null); webApplication.setAttribute(Policy.class.getName(), policy); InternalPolicyThreadLocal.setPolicy(policy); webApplication.addListener(InternalPolicyServletContextListener.class.getName()); webApplication.addListener(InternalPolicyServletRequestListener.class.getName()); } catch (NoSuchAlgorithmException ex) { LOGGER.log(WARNING, "Error setting up Policy", ex); } } else { LOGGER.log(TRACE, "Policy extension is disabled"); } } } ================================================ FILE: extension/policy/src/main/java/cloud/piranha/extension/policy/internal/InternalPolicyServletContextListener.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.policy.internal; import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletContextListener; import static java.lang.System.Logger.Level.DEBUG; /** * The ServletContextListener used to remove the Policy instance once * initialization is done. * * @author Manfred Riem (mriem@manorrock.com) */ public class InternalPolicyServletContextListener implements ServletContextListener { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(InternalPolicyServletContextListener.class.getName()); @Override public void contextInitialized(ServletContextEvent event) { LOGGER.log(DEBUG, "Removing Policy"); InternalPolicyThreadLocal.removePolicy(); } } ================================================ FILE: extension/policy/src/main/java/cloud/piranha/extension/policy/internal/InternalPolicyServletRequestListener.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.policy.internal; import cloud.piranha.core.api.WebApplication; import jakarta.servlet.ServletRequestEvent; import jakarta.servlet.ServletRequestListener; import static java.lang.System.Logger.Level.DEBUG; import java.security.Policy; /** * The ServletRequestListener that sets the Policy instance on the current * thread just before request processing and removes the Policy instance from * the current after the request has been processed. * * @author Manfred Riem (mriem@manorrock.com) */ public class InternalPolicyServletRequestListener implements ServletRequestListener { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(InternalPolicyServletRequestListener.class.getName()); @Override public void requestDestroyed(ServletRequestEvent event) { LOGGER.log(DEBUG, "Removing Policy"); InternalPolicyThreadLocal.removePolicy(); } @SuppressWarnings("removal") @Override public void requestInitialized(ServletRequestEvent event) { LOGGER.log(DEBUG, "Setting Policy"); WebApplication webApplication = (WebApplication) event.getServletContext(); Policy policy = (Policy) webApplication.getAttribute(Policy.class.getName()); InternalPolicyThreadLocal.setPolicy(policy); } } ================================================ FILE: extension/policy/src/main/java/cloud/piranha/extension/policy/internal/InternalPolicyThreadLocal.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.policy.internal; import java.security.Policy; import java.util.HashMap; /** * The class to keep track of setting/removing the thread local for Policy. * * @author Manfred Riem (mriem@manorrock.com) */ public class InternalPolicyThreadLocal { /** * Stores the policies by thread id. */ @SuppressWarnings("removal") private static final HashMap POLICIES = new HashMap<>(1); /** * Remove the policy. */ @SuppressWarnings("deprecation") public static void removePolicy() { POLICIES.remove(Thread.currentThread().getId()); } /** * Set the policy. * * @param policy the policy. */ @SuppressWarnings({ "removal", "deprecation" }) public static void setPolicy(Policy policy) { POLICIES.put(Thread.currentThread().getId(), policy); } } ================================================ FILE: extension/policy/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Java Policy integration extension. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.policy { exports cloud.piranha.extension.policy; opens cloud.piranha.extension.policy; requires transitive cloud.piranha.core.api; } ================================================ FILE: extension/policy/src/test/java/cloud/piranha/extension/policy/PolicyExtensionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.policy; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.embedded.EmbeddedRequest; import cloud.piranha.embedded.EmbeddedResponse; import jakarta.servlet.ServletRequestEvent; import jakarta.servlet.ServletRequestListener; import java.security.Policy; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.Test; /** * The JUnit tests for the PolicyExtension class. * * @author Manfred Riem (mriem@manorrock.com) */ class PolicyExtensionTest { /** * Test configure method. * * @throws Exception when a serious error occurs. */ @Test void testConfigure() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); PolicyExtension extension = new PolicyExtension(); extension.configure(webApplication); webApplication.initialize(); webApplication.start(); EmbeddedRequest request = new EmbeddedRequest(); request.setWebApplication(webApplication); EmbeddedResponse response = new EmbeddedResponse(); response.setWebApplication(webApplication); webApplication.service(request, response); assertEquals(200, response.getStatus()); } /** * Test to validate Policy was not set when PolicyExtension is disabled. * * @throws Exception when a serious error occurs. */ @Test void testValidatePolicyNotSet() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setAttribute(PolicyExtension.class.getName() + ".disable", "true"); PolicyExtension extension = new PolicyExtension(); extension.configure(webApplication); webApplication.addListener(new ServletRequestListener() { @SuppressWarnings("removal") @Override public void requestInitialized(ServletRequestEvent sre) { assertNull(webApplication.getAttribute(Policy.class.getName())); }; }); webApplication.initialize(); webApplication.start(); EmbeddedRequest request = new EmbeddedRequest(); request.setWebApplication(webApplication); EmbeddedResponse response = new EmbeddedResponse(); response.setWebApplication(webApplication); webApplication.service(request, response); assertEquals(200, response.getStatus()); } /** * Test to validate Policy was set when PolicyExtension is enabled. * * @throws Exception when a serious error occurs. */ @Test void testValidatePolicySet() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); PolicyExtension extension = new PolicyExtension(); extension.configure(webApplication); webApplication.addListener(new ServletRequestListener() { @SuppressWarnings("removal") @Override public void requestInitialized(ServletRequestEvent sre) { assertNotNull(webApplication.getAttribute(Policy.class.getName())); }; }); webApplication.initialize(); webApplication.start(); EmbeddedRequest request = new EmbeddedRequest(); request.setWebApplication(webApplication); EmbeddedResponse response = new EmbeddedResponse(); response.setWebApplication(webApplication); webApplication.service(request, response); assertEquals(200, response.getStatus()); } } ================================================ FILE: extension/pom.xml ================================================ 4.0.0 cloud.piranha project 25.4.0-SNAPSHOT cloud.piranha.extension project pom Piranha - Extension annotationscan annotationscan-classfile angus bytesstreamhandler concurro coreprofile declared eclipselink epicyro exousia expressly fileupload handlestypes hazelcast herring hibernate-validator jaxb jersey jstl micro microprofile mojarra naming-cdi omnifaces-config omnifaces-microprofile-jwt-auth omnifaces-omniservices omnifaces-omniutils omnifish-omnibeans omnifish-transact parsson platform policy scinitializer security-servlet security-jakarta servlet servletannotations soteria tempdir tyrus webprofile webxml welcomefile weld yasson wasp datasource cloud.piranha bom ${project.version} pom import org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test default file:///tmp/piranha/extension/ ================================================ FILE: extension/scinitializer/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-scinitializer jar Piranha - Extension - ServletContainerInitializer cloud.piranha.core piranha-core-api ${project.version} compile cloud.piranha.core piranha-core-impl ${project.version} test org.junit.jupiter junit-jupiter-api test org.junit.platform junit-platform-launcher test default file:///tmp/piranha/extension/scinitializer ================================================ FILE: extension/scinitializer/src/main/java/cloud/piranha/extension/scinitializer/ServletContainerInitializerExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.scinitializer; import static java.lang.System.Logger.Level.DEBUG; import static java.lang.System.Logger.Level.TRACE; import static java.util.Collections.emptyList; import java.util.ArrayList; import java.util.List; import java.util.ServiceLoader; import java.lang.System.Logger; import jakarta.servlet.ServletContainerInitializer; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; /** * The WebApplication extension that enables ServletContainerInitializer * processing. * * @author Manfred Riem (mriem@manorrock.com) */ public class ServletContainerInitializerExtension implements WebApplicationExtension { /** * Stores the ignore initializers property. */ public static final String IGNORE_INITIALIZERS_PROPERTY = ServletContainerInitializerExtension.class.getName() + ".ignoreInitializers"; /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(ServletContainerInitializerExtension.class.getPackage().getName()); /** * Stores the exclude existing initializers flag. */ private final boolean excludeExistingInitializers; /** * Stores the initializers to be ignored. */ private final List ignoreInitializers; /** * Constructor. */ public ServletContainerInitializerExtension() { this(false, emptyList()); } /** * Constructor. * * @param excludeExistingInitializers the exclude existing initializers * flag. * @param ignoreInitializers ignore the given initializers. */ public ServletContainerInitializerExtension(boolean excludeExistingInitializers, List ignoreInitializers) { this.excludeExistingInitializers = excludeExistingInitializers; this.ignoreInitializers = new ArrayList<>(ignoreInitializers); } /** * Configure the web application. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { LOGGER.log(TRACE, "Configuring the ServletContainerInitializer extension"); if (Boolean.parseBoolean(System.getProperty( "cloud.piranha.extension.scinitializer.ServletContainerInitializerExtensionp.enabled", "true"))) { if (System.getProperty(IGNORE_INITIALIZERS_PROPERTY) != null) { String[] initializers = System.getProperty(IGNORE_INITIALIZERS_PROPERTY).split(","); if (initializers.length > 0) { for(int i=0; i serviceLoader = ServiceLoader.load( ServletContainerInitializer.class, webApplication.getClassLoader()); for (ServletContainerInitializer initializer : serviceLoader) { LOGGER.log(DEBUG, () -> "Adding initializer: " + initializer.getClass().getName()); if (shouldAdd(webApplication, initializer)) { webApplication.addInitializer(initializer); } } if (getClass().getModule().isNamed()) { // We are running in a modular environment, // the providers from modules aren't available in the webApplication classloader serviceLoader = ServiceLoader.load(ServletContainerInitializer.class); for (ServletContainerInitializer initializer : serviceLoader) { LOGGER.log(DEBUG, () -> "Adding initializer: " + initializer.getClass().getName()); if (shouldAdd(webApplication, initializer)) { webApplication.addInitializer(initializer); } } } } } private boolean shouldAdd(WebApplication webApplication, ServletContainerInitializer initializer) { if (isIgnored(initializer)) { return false; } if (!excludeExistingInitializers) { return true; } return !containsInstance(webApplication, initializer); } private boolean containsInstance(WebApplication webApplication, ServletContainerInitializer initializer) { return webApplication.getInitializers() .stream() .anyMatch(e -> e.getClass().equals(initializer.getClass())); } private boolean isIgnored(ServletContainerInitializer initializer) { return ignoreInitializers .stream() .anyMatch(e -> e.equals(initializer.getClass().getName())); } } ================================================ FILE: extension/scinitializer/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the ServletContainerInitializer extension. * * *

* The following property can be used to influence the workings of this module. *

* * * * * * * * * * *
PropertyNotes
cloud.piranha.extension.scinitializer.ServletContainerInitializerExtension.enabledtrue to enable (default), false to disable
Configurable properties
* * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.scinitializer { exports cloud.piranha.extension.scinitializer; opens cloud.piranha.extension.scinitializer; requires transitive cloud.piranha.core.api; uses jakarta.servlet.ServletContainerInitializer; } ================================================ FILE: extension/scinitializer/src/test/java/cloud/piranha/extension/scinitializer/ServletContainerInitializerExtensionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.scinitializer; import cloud.piranha.core.impl.DefaultWebApplication; import java.util.ArrayList; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; /** * The JUnit tests for the ServletContainerInitializerExtension class. * * @author Manfred Riem (mriem@manorrock.com) */ class ServletContainerInitializerExtensionTest { /** * Test configure method. */ @Test void testConfigure() { DefaultWebApplication webApplication = new DefaultWebApplication(); ServletContainerInitializerExtension extension = new ServletContainerInitializerExtension(); extension.configure(webApplication); webApplication.initialize(); assertTrue(webApplication.isInitialized()); } /** * Test configure method. */ @Test void testConfigure2() { DefaultWebApplication webApplication = new DefaultWebApplication(); ArrayList ignoredInitializers = new ArrayList<>(); ignoredInitializers.add("org.glassfish.tyrus.servlet.TyrusServletContainerInitializer"); ServletContainerInitializerExtension extension = new ServletContainerInitializerExtension( true, ignoredInitializers); extension.configure(webApplication); webApplication.initialize(); assertTrue(webApplication.isInitialized()); } /** * Test configure method. */ @Test void testConfigure3() { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addInitializer("org.glassfish.tyrus.servlet.TyrusServletContainerInitializer"); ArrayList ignoredInitializers = new ArrayList<>(); ServletContainerInitializerExtension extension = new ServletContainerInitializerExtension( true, ignoredInitializers); extension.configure(webApplication); webApplication.initialize(); assertTrue(webApplication.isInitialized()); } } ================================================ FILE: extension/security-jakarta/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-security-jakarta jar Piranha - Extension - Security - Jakarta cloud.piranha.extension piranha-extension-security-servlet ${project.version} compile cloud.piranha.extension piranha-extension-soteria ${project.version} compile cloud.piranha.core piranha-core-impl ${project.version} provided cloud.piranha.extension piranha-extension-weld ${project.version} provided ================================================ FILE: extension/security-jakarta/src/main/java/cloud/piranha/extension/security/jakarta/JakartaSecurityAllInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.security.jakarta; import static cloud.piranha.extension.exousia.AuthorizationPreInitializer.AUTHZ_FACTORY_CLASS; import static cloud.piranha.extension.exousia.AuthorizationPreInitializer.AUTHZ_POLICY_CLASS; import java.util.Set; import org.glassfish.exousia.modules.def.DefaultPolicy; import org.glassfish.exousia.modules.def.DefaultPolicyConfigurationFactory; import cloud.piranha.extension.epicyro.AuthenticationInitializer; import cloud.piranha.extension.exousia.AuthorizationInitializer; import cloud.piranha.extension.exousia.AuthorizationPreInitializer; import cloud.piranha.extension.soteria.SoteriaInitializer; import cloud.piranha.extension.soteria.SoteriaPreCDIInitializer; import cloud.piranha.extension.weld.WeldInitializer; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; /** * The Jakarta Security All initializer. * * @author Arjan Tijms */ public class JakartaSecurityAllInitializer implements ServletContainerInitializer { /** * Stores the initializers. */ ServletContainerInitializer[] initializers = { // Makes web.xml login-config available to Soteria new SoteriaPreCDIInitializer(), // Configures the security constraints, authorization module // and authorization filter that checks constraints before authentication new AuthorizationPreInitializer(), // Configures the authentication module and authentication filter new AuthenticationInitializer(), // Configures the authorization filter that checks constraints after authentication new AuthorizationInitializer(), // Initializes CDI, on which Jakarta Security is also based new WeldInitializer(), // Configures Soteria, which implements Jakarta Security and sets itself as a Servlet // authentication module new SoteriaInitializer(), }; /** * Constructor. */ public JakartaSecurityAllInitializer() { } /** * Initialize Jakarta Security * * @param classes the classes. * @param servletContext the Servlet context. * @throws ServletException when a Servlet error occurs. */ @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { servletContext.setAttribute(AUTHZ_FACTORY_CLASS, DefaultPolicyConfigurationFactory.class); servletContext.setAttribute(AUTHZ_POLICY_CLASS, DefaultPolicy.class); for (ServletContainerInitializer initializer : initializers) { initializer.onStartup(classes, servletContext); } } } ================================================ FILE: extension/security-jakarta/src/main/java/cloud/piranha/extension/security/jakarta/JakartaSecurityExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.security.jakarta; import static java.lang.System.Logger.Level.WARNING; import java.lang.System.Logger; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import jakarta.servlet.ServletContainerInitializer; /** * The extension for Jakarta Security. * * @author Thiago Henrique Hupner */ public class JakartaSecurityExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(JakartaSecurityExtension.class.getName()); /** * Constructor. */ public JakartaSecurityExtension() { } /** * Configure the web application. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { try { webApplication.addInitializer( webApplication.getClassLoader() .loadClass(JakartaSecurityAllInitializer.class.getName()) .asSubclass(ServletContainerInitializer.class) .getDeclaredConstructor() .newInstance()); } catch (ReflectiveOperationException | SecurityException | IllegalArgumentException ex) { LOGGER.log(WARNING, "Unable to enable the JakartaSecurityExtension", ex); } } } ================================================ FILE: extension/security-jakarta/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Jakarta Security integration extension. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.security.jakarta { exports cloud.piranha.extension.security.jakarta; opens cloud.piranha.extension.security.jakarta; requires cloud.piranha.core.api; requires cloud.piranha.core.impl; requires cloud.piranha.extension.epicyro; requires cloud.piranha.extension.security.servlet; requires transitive cloud.piranha.extension.soteria; requires cloud.piranha.extension.weld; requires java.naming; requires org.glassfish.epicyro; requires org.glassfish.exousia; } ================================================ FILE: extension/security-servlet/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-security-servlet jar Piranha - Extension - Security - Servlet org.jacoco jacoco-maven-plugin check check BUNDLE INSTRUCTION COVEREDRATIO 0.13 BRANCH COVEREDRATIO 0.00 cloud.piranha.core piranha-core-impl ${project.version} compile cloud.piranha.extension piranha-extension-epicyro ${project.version} compile jakarta.servlet jakarta.servlet-api cloud.piranha.extension piranha-extension-exousia ${project.version} compile org.junit.jupiter junit-jupiter-api test ================================================ FILE: extension/security-servlet/src/main/java/cloud/piranha/extension/security/servlet/ServletSecurityAllInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.security.servlet; import static cloud.piranha.extension.exousia.AuthorizationPreInitializer.AUTHZ_FACTORY_CLASS; import static cloud.piranha.extension.exousia.AuthorizationPreInitializer.AUTHZ_POLICY_CLASS; import java.util.Set; import org.glassfish.exousia.modules.def.DefaultPolicy; import org.glassfish.exousia.modules.def.DefaultPolicyConfigurationFactory; import cloud.piranha.extension.epicyro.AuthenticationInitializer; import cloud.piranha.extension.exousia.AuthorizationInitializer; import cloud.piranha.extension.exousia.AuthorizationPostInitializer; import cloud.piranha.extension.exousia.AuthorizationPreInitializer; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; /** * The Servlet Security All initializer. * * @author Arjan Tijms */ public class ServletSecurityAllInitializer implements ServletContainerInitializer { /** * Stores the initializers. */ ServletContainerInitializer[] initializers = { // Configures the security constraints, authorization module // and authorization filter that checks constraints before authentication new AuthorizationPreInitializer(), // Configures the authentication module and authentication filter new AuthenticationInitializer(), // Configures the authorization filter that checks constraints after authentication new AuthorizationInitializer(), // Inits the authorization server. Note this should always run after all Servlets in the application // have been discovered/added. new AuthorizationPostInitializer(), }; /** * Constructor. */ public ServletSecurityAllInitializer() { } /** * Initialize Jakarta Security * * @param classes the classes. * @param servletContext the Servlet context. * @throws ServletException when a Servlet error occurs. */ @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { // The default Servlet Authorization module, which implements authorization as defined // by the Jakarta Servlet specifications servletContext.setAttribute(AUTHZ_FACTORY_CLASS, DefaultPolicyConfigurationFactory.class); servletContext.setAttribute(AUTHZ_POLICY_CLASS, DefaultPolicy.class); for (ServletContainerInitializer initializer : initializers) { initializer.onStartup(classes, servletContext); } } } ================================================ FILE: extension/security-servlet/src/main/java/cloud/piranha/extension/security/servlet/ServletSecurityExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.security.servlet; import java.lang.System.Logger; import java.lang.System.Logger.Level; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import jakarta.servlet.ServletContainerInitializer; /** * The extension for Servlet Security. * * @author Thiago Henrique Hupner */ public class ServletSecurityExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(ServletSecurityExtension.class.getName()); /** * Constructor. */ public ServletSecurityExtension() { } /** * Configure the web application. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { try { ServletContainerInitializer initializer = webApplication.getClassLoader() .loadClass(ServletSecurityAllInitializer.class.getName()) .asSubclass(ServletContainerInitializer.class) .getDeclaredConstructor() .newInstance(); // Find and remove previous version webApplication.getInitializers() .stream() .filter(e -> e.getClass().getName().endsWith("SecurityInitializer")) .findFirst() .ifPresent(securityInitializer -> webApplication.getInitializers().remove(securityInitializer)); webApplication.addInitializer(initializer); } catch (ReflectiveOperationException | SecurityException | IllegalArgumentException ex) { LOGGER.log(Level.WARNING, "Unable to enable the ServletSecurityExtension", ex); } } } ================================================ FILE: extension/security-servlet/src/main/java/cloud/piranha/extension/security/servlet/ServletSecurityManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.security.servlet; import static cloud.piranha.extension.epicyro.AuthenticationInitializer.AUTH_SERVICE; import static cloud.piranha.extension.exousia.AuthorizationPreInitializer.AUTHZ_SERVICE; import static cloud.piranha.core.api.SecurityManager.AuthenticateSource.MID_REQUEST_USER; import static cloud.piranha.core.impl.DefaultAuthenticatedIdentity.getCurrentSubject; import static java.util.Arrays.asList; import java.io.IOException; import java.security.Principal; import java.util.Collection; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.glassfish.exousia.AuthorizationService; import cloud.piranha.core.api.AuthenticatedIdentity; import cloud.piranha.core.api.SecurityConstraint; import cloud.piranha.core.api.SecurityManager; import cloud.piranha.core.api.SecurityRoleReference; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationRequest; import cloud.piranha.core.impl.DefaultAuthenticatedIdentity; import cloud.piranha.core.impl.DefaultServletEnvironment; import jakarta.servlet.ServletConfig; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.glassfish.epicyro.config.helper.Caller; import org.glassfish.epicyro.services.DefaultAuthenticationService; /** * SecurityManager implementation that uses Servlet Security semantics. * * @author Arjan Tijms * @author Manfred Riem (mriem@manorrock.com) */ public class ServletSecurityManager implements SecurityManager { /** * Stores the auth method. */ protected String authMethod; /** * Stores if we are denying uncovered HTTP methods. */ protected boolean denyUncoveredHttpMethods; /** * Stores the form error page. */ protected String formErrorPage; /** * Stores the form login page. */ protected String formLoginPage; /** * Stores the realm name. */ protected String realmName; /** * Stores all declared roles in the application */ protected final Set roles = ConcurrentHashMap.newKeySet(); /** * Stores the security constraints. */ protected List securityConstraints; /** * Stores the security role references. */ protected Map> securityRoleReferences; /** * Handler for the specific HttpServletRequest#login method call */ protected UsernamePasswordLoginHandler usernamePasswordLoginHandler; /** * Stores the web application. */ protected WebApplication webApplication; /** * Constructor. */ public ServletSecurityManager() { securityConstraints = new ArrayList<>(); securityRoleReferences = new HashMap<>(); } @Override public boolean authenticate(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { return authenticate(request, response, MID_REQUEST_USER); } @Override public boolean authenticate(HttpServletRequest request, HttpServletResponse response, AuthenticateSource source) throws IOException, ServletException { DefaultAuthenticationService authenticationService = (DefaultAuthenticationService) request.getServletContext().getAttribute(AUTH_SERVICE); Caller storedCaller = null; HttpSession session = request.getSession(false); if (session != null) { storedCaller = (Caller) session.getAttribute(".caller"); if (storedCaller != null) { WebApplicationRequest webApplicationRequest = (WebApplicationRequest) request; webApplicationRequest.setUserPrincipal(new ServletSecurityPrincipal(storedCaller.getName())); } } boolean mandatory = true; if (source != MID_REQUEST_USER) { mandatory = !isRequestedResourcePublic(request); } Caller caller = authenticationService.validateRequest( request, response, source == MID_REQUEST_USER, mandatory); // Caller is null means authentication failed. If authentication did not happen (auth module decided to do nothing) // we have a caller instance with a null caller principal if (caller == null) { return false; } if (caller.getCallerPrincipal() instanceof ServletSecurityPrincipal) { caller = storedCaller; } if (authenticationService.mustRegisterSession(request, response)) { request.getSession().setAttribute(".caller", caller); } if (caller != null && caller.getCallerPrincipal() != null) { setIdentityForCurrentRequest(request, caller.getCallerPrincipal(), caller.getGroups(), "authenticate"); } return caller != null; } @Override public void declareRoles(String[] roles) { this.roles.addAll(asList(roles)); } @Override public void declareRoles(Collection roles) { if (roles == null) { return; } this.roles.addAll(roles); } @Override public String getAuthMethod() { return authMethod; } @Override public HttpServletRequest getAuthenticatedRequest(HttpServletRequest request, HttpServletResponse response) { return getAuthenticationService(request).getWrappedRequestIfSet(request, response); } @Override public HttpServletResponse getAuthenticatedResponse(HttpServletRequest request, HttpServletResponse response) { return getAuthenticationService(request).getWrappedResponseIfSet(request, response); } /** * Get the authentication service. * * @param request the request. * @return the authentication service. */ protected DefaultAuthenticationService getAuthenticationService(HttpServletRequest request) { return (DefaultAuthenticationService) request.getServletContext().getAttribute(AUTH_SERVICE); } /** * Get the authorization service. * * @param request the request. * @return the authorization service. */ protected AuthorizationService getAuthorizationService(HttpServletRequest request) { return (AuthorizationService) request.getServletContext().getAttribute(AUTHZ_SERVICE); } @Override public boolean getDenyUncoveredHttpMethods() { return denyUncoveredHttpMethods; } @Override public String getFormErrorPage() { return formErrorPage; } @Override public String getFormLoginPage() { return formLoginPage; } @Override public String getRealmName() { return realmName; } @Override public Set getRoles() { return roles; } @Override public List getSecurityConstraints() { return securityConstraints; } @Override public Map> getSecurityRoleReferences() { return securityRoleReferences; } private String getServletName(HttpServletRequest request) { ServletConfig servletConfig = (ServletConfig) request.getAttribute(DefaultServletEnvironment.class.getName()); if (servletConfig != null && servletConfig.getServletName() != null) { return servletConfig.getServletName(); } return ""; } @Override public UsernamePasswordLoginHandler getUsernamePasswordLoginHandler() { return usernamePasswordLoginHandler; } @Override public WebApplication getWebApplication() { return webApplication; } @Override public boolean isCallerAuthorizedForResource(HttpServletRequest request) { return getAuthorizationService(request).checkWebResourcePermission(request); } @Override public boolean isRequestedResourcePublic(HttpServletRequest request) { return getAuthorizationService(request).checkPublicWebResourcePermission(request); } @Override public boolean isRequestSecurityAsRequired(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if (getAuthorizationService(request) != null) { return getAuthorizationService(request).checkWebUserDataPermission(request); } else { return false; } } @Override public boolean isUserInRole(HttpServletRequest request, String role) { return getAuthorizationService(request).checkWebRoleRefPermission(getServletName(request), role); } @Override public void login(HttpServletRequest request, String username, String password) throws ServletException { AuthenticatedIdentity resultIdentity = usernamePasswordLoginHandler.login(request, username, password); if (resultIdentity == null) { throw new ServletException(); } setIdentityForCurrentRequest(request, resultIdentity.getCallerPrincipal(), resultIdentity.getGroups(), "login"); } @Override public void logout(HttpServletRequest request, HttpServletResponse response) throws ServletException { getAuthenticationService(request).clearSubject(request, response, getCurrentSubject()); WebApplicationRequest webApplicationRequest = (WebApplicationRequest) request; webApplicationRequest.setUserPrincipal(null); webApplicationRequest.setAuthType(null); DefaultAuthenticatedIdentity.clear(); } @Override public void postRequestProcess(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { getAuthenticationService(request).secureResponse(request, response); } @Override public void setAuthMethod(String authMethod) { this.authMethod = authMethod; } @Override public void setDenyUncoveredHttpMethods(boolean denyUncoveredHttpMethods) { this.denyUncoveredHttpMethods = denyUncoveredHttpMethods; } @Override public void setFormErrorPage(String formErrorPage) { this.formErrorPage = formErrorPage; } @Override public void setFormLoginPage(String formLoginPage) { this.formLoginPage = formLoginPage; } private void setIdentityForCurrentRequest(HttpServletRequest request, Principal callerPrincipal, Set groups, String authType) { Principal currentPrincipal = callerPrincipal == null ? null : callerPrincipal.getName() == null ? null : callerPrincipal; WebApplicationRequest webApplicationRequest = (WebApplicationRequest) request; webApplicationRequest.setUserPrincipal(currentPrincipal); if (currentPrincipal != null) { webApplicationRequest.setAuthType(authType); } DefaultAuthenticatedIdentity.setCurrentIdentity(currentPrincipal, groups); } @Override public void setRealmName(String realmName) { this.realmName = realmName; } @Override public void setSecurityConstraints(List securityConstraints) { this.securityConstraints = securityConstraints; } @Override public void setSecurityRoleReferences(Map> securityRoleReferences) { this.securityRoleReferences = securityRoleReferences; } @Override public void setUsernamePasswordLoginHandler(UsernamePasswordLoginHandler usernamePasswordLoginHandler) { this.usernamePasswordLoginHandler = usernamePasswordLoginHandler; } @Override public void setWebApplication(WebApplication webApplication) { this.webApplication = webApplication; } } ================================================ FILE: extension/security-servlet/src/main/java/cloud/piranha/extension/security/servlet/ServletSecurityManagerExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.security.servlet; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import jakarta.servlet.ServletContainerInitializer; import java.lang.System.Logger; import static java.lang.System.Logger.Level.DEBUG; /** * The WebApplicationExtension that adds the ServletSecurityManagerInitializer. * * @author Manfred Riem (mriem@manorrock.com) */ public class ServletSecurityManagerExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger( ServletSecurityManagerExtension.class.getName()); /** * Constructor. */ public ServletSecurityManagerExtension() { } @Override public void configure(WebApplication webApplication) { LOGGER.log(DEBUG, "Adding ServletSecurityManagerInitializer"); try { ServletContainerInitializer initializer = webApplication.getClassLoader() .loadClass(ServletSecurityManagerInitializer.class.getName()) .asSubclass(ServletContainerInitializer.class) .getDeclaredConstructor() .newInstance(); webApplication.addInitializer(initializer); } catch (ReflectiveOperationException | SecurityException | IllegalArgumentException ex) { LOGGER.log(Logger.Level.WARNING, "Unable to add the ServletSecurityManagerInitializer", ex); } } } ================================================ FILE: extension/security-servlet/src/main/java/cloud/piranha/extension/security/servlet/ServletSecurityManagerInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.security.servlet; import cloud.piranha.core.api.WebApplication; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import java.lang.System.Logger; import static java.lang.System.Logger.Level.DEBUG; import java.util.Set; /** * The ServletContainerInitializer that sets the ServletSecurityManager. * * @author Manfred Riem (mriem@manorrock.com) */ public class ServletSecurityManagerInitializer implements ServletContainerInitializer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger( ServletSecurityManagerInitializer.class.getName()); /** * Constructor. */ public ServletSecurityManagerInitializer() { } @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { LOGGER.log(DEBUG, "Set the ServletSecurityManager"); WebApplication webApplication = (WebApplication) servletContext; webApplication.getManager().setSecurityManager(new ServletSecurityManager()); } } ================================================ FILE: extension/security-servlet/src/main/java/cloud/piranha/extension/security/servlet/ServletSecurityPrincipal.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.security.servlet; import java.security.Principal; /** * A principal used by ServletSecurityManager. * * @author Manfred Riem (mriem@manorrock.com) */ public class ServletSecurityPrincipal implements Principal { /** * Stores the name */ private final String name; /** * Constructor. * * @param name the name. */ public ServletSecurityPrincipal(String name) { this.name = name; } @Override public String getName() { return name; } } ================================================ FILE: extension/security-servlet/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Servlet Security integration extension. * * @author Arjan Tijms * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.security.servlet { exports cloud.piranha.extension.security.servlet; opens cloud.piranha.extension.security.servlet; requires transitive cloud.piranha.core.api; requires cloud.piranha.core.impl; requires transitive cloud.piranha.extension.exousia; requires transitive cloud.piranha.extension.epicyro; requires java.naming; requires org.glassfish.epicyro; requires org.glassfish.exousia; } ================================================ FILE: extension/security-servlet/src/test/java/cloud/piranha/extension/security/servlet/ServletSecurityManagerExtensionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.security.servlet; import cloud.piranha.core.impl.DefaultWebApplication; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the ServletSecurityManagerExtension class. * * @author Manfred Riem (mriem@manorrock.com) */ class ServletSecurityManagerExtensionTest { /** * Test configure method. */ @Test void testConfigure() { DefaultWebApplication webApplication = new DefaultWebApplication(); ServletSecurityManagerExtension extension = new ServletSecurityManagerExtension(); extension.configure(webApplication); assertTrue(!webApplication.getInitializers().isEmpty()); } } ================================================ FILE: extension/security-servlet/src/test/java/cloud/piranha/extension/security/servlet/ServletSecurityManagerInitializerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.security.servlet; import cloud.piranha.core.impl.DefaultWebApplication; import jakarta.servlet.ServletException; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; /** * The JUnit tests for the ServletSecurityManagerInitializer class. * * @author Manfred Riem (mriem@manorrock.com) */ class ServletSecurityManagerInitializerTest { @Test void testOnStartup() { try { DefaultWebApplication webApplication = new DefaultWebApplication(); ServletSecurityManagerInitializer initializer = new ServletSecurityManagerInitializer(); initializer.onStartup(null, webApplication); assertTrue(webApplication.getManager().getSecurityManager() instanceof ServletSecurityManager); } catch (ServletException ex) { fail(); } } } ================================================ FILE: extension/security-servlet/src/test/java/cloud/piranha/extension/security/servlet/ServletSecurityManagerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.security.servlet; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * The JUnit tests for the ServletSecurityManager class. * * @author Manfred Riem (mriem@manorrock.com) */ public class ServletSecurityManagerTest { /** * Test getSecurityConstraints method. */ @Test public void testGetSecurityConstraints() { ServletSecurityManager manager = new ServletSecurityManager(); assertNotNull(manager.getSecurityConstraints()); manager.setSecurityConstraints(null); assertNull(manager.getSecurityConstraints()); } } ================================================ FILE: extension/servlet/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-servlet jar Piranha - Extension - Servlet This module delivers the default set of extensions for a runtime equivalent to prevalent Servlet containers. cloud.piranha.core piranha-core-api ${project.version} compile cloud.piranha.extension piranha-extension-annotationscan ${project.version} compile cloud.piranha.extension piranha-extension-annotationscan-classfile ${project.version} compile cloud.piranha.extension piranha-extension-declared ${project.version} compile cloud.piranha.extension piranha-extension-fileupload ${project.version} compile cloud.piranha.extension piranha-extension-expressly ${project.version} compile cloud.piranha.extension piranha-extension-tyrus ${project.version} compile cloud.piranha.extension piranha-extension-herring ${project.version} compile cloud.piranha.extension piranha-extension-wasp ${project.version} compile cloud.piranha.extension piranha-extension-policy ${project.version} compile cloud.piranha.extension piranha-extension-scinitializer ${project.version} compile cloud.piranha.extension piranha-extension-security-servlet ${project.version} compile cloud.piranha.extension piranha-extension-servletannotations ${project.version} compile cloud.piranha.extension piranha-extension-tempdir ${project.version} compile cloud.piranha.extension piranha-extension-webxml ${project.version} compile cloud.piranha.extension piranha-extension-welcomefile ${project.version} compile jakarta.annotation jakarta.annotation-api runtime ================================================ FILE: extension/servlet/src/main/java/cloud/piranha/extension/servlet/ServletExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.servlet; import cloud.piranha.core.api.WebApplicationExtension; import cloud.piranha.core.api.WebApplicationExtensionContext; import cloud.piranha.extension.annotationscan.AnnotationScanExtension; import cloud.piranha.extension.annotationscan.classfile.ClassfileAnnotationScanExtension; import cloud.piranha.extension.declared.DeclaredExtension; import cloud.piranha.extension.fileupload.FileUploadExtension; import cloud.piranha.extension.herring.HerringExtension; import cloud.piranha.extension.policy.PolicyExtension; import cloud.piranha.extension.scinitializer.ServletContainerInitializerExtension; import cloud.piranha.extension.security.servlet.ServletSecurityExtension; import cloud.piranha.extension.security.servlet.ServletSecurityManagerExtension; import cloud.piranha.extension.servletannotations.ServletAnnotationsExtension; import cloud.piranha.extension.tempdir.TempDirExtension; import cloud.piranha.extension.wasp.WaspExtension; import cloud.piranha.extension.wasp.WaspJspManagerExtension; import cloud.piranha.extension.webxml.WebXmlExtension; import cloud.piranha.extension.welcomefile.WelcomeFileExtension; /** * The extension that delivers the extensions for Piranha Micro/Server. * * @author Manfred Riem (mriem@manorrock.com) */ public class ServletExtension implements WebApplicationExtension { /** * Constructor. */ public ServletExtension() { } @Override public void extend(WebApplicationExtensionContext context) { context.add(PolicyExtension.class); // JavaPolicy context.add(TempDirExtension.class); // TEMPDIR context.add(WelcomeFileExtension.class); // welcome-file context.add(ServletSecurityManagerExtension.class); // SecurityManager context.add(FileUploadExtension.class); // Servlet Part API support context.add(WaspJspManagerExtension.class); // addJspFile context.add(HerringExtension.class); // Herring (JNDI) context.add(WebXmlExtension.class); // web.xml context.add(getAnnotationScanExtensionClass()); // Annotation scanning context.add(ServletAnnotationsExtension.class); // Servlet annotations context.add(DeclaredExtension.class); // context.add(WaspExtension.class); // WaSP context.add(ServletContainerInitializerExtension.class); // ServletContainerInitializer context.add(ServletSecurityExtension.class); // Security implementation } private static Class getAnnotationScanExtensionClass() { if (System.getProperty(ClassfileAnnotationScanExtension.EXPERIMENTAL_PROPERTY) != null) { return ClassfileAnnotationScanExtension.class; // Annotation scanning using the new Classfile API } return AnnotationScanExtension.class; // Annotation scanning } } ================================================ FILE: extension/servlet/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the meta extension for a Servlet container. * *

* The following extensions and/or dependencies are delivered as part of this * meta extension: *

*
    *
  • Annotation Scanning
  • *
  • File Upload
  • *
  • Herring (JNDI)
  • *
  • Java Policy
  • *
  • ServletContainerInitializer
  • *
  • Servlet Security
  • *
  • Servlet Annotations
  • *
  • TEMPDIR
  • *
  • WaSP (Pages)
  • *
  • web.xml
  • *
  • Welcome File
  • *
*/ module cloud.piranha.extension.servlet { exports cloud.piranha.extension.servlet; opens cloud.piranha.extension.servlet; requires transitive cloud.piranha.core.api; requires cloud.piranha.extension.annotationscan; requires cloud.piranha.extension.annotationscan.classfile; requires cloud.piranha.extension.declared; requires cloud.piranha.extension.fileupload; requires cloud.piranha.extension.herring; requires cloud.piranha.extension.expressly; requires cloud.piranha.extension.policy; requires cloud.piranha.extension.scinitializer; requires cloud.piranha.extension.security.servlet; requires cloud.piranha.extension.servletannotations; requires cloud.piranha.extension.tempdir; requires cloud.piranha.extension.wasp; requires cloud.piranha.extension.webxml; requires cloud.piranha.extension.welcomefile; } ================================================ FILE: extension/servletannotations/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-servletannotations jar Piranha - Extension - Servlet Annotations cloud.piranha.core piranha-core-api ${project.version} compile jakarta.annotation jakarta.annotation-api compile cloud.piranha piranha-embedded ${project.version} test cloud.piranha.core piranha-core-impl ${project.version} test cloud.piranha.extension piranha-extension-annotationscan ${project.version} test cloud.piranha.extension piranha-extension-webxml ${project.version} test cloud.piranha.resource piranha-resource-impl ${project.version} test org.junit.jupiter junit-jupiter-engine test org.junit.platform junit-platform-launcher test ================================================ FILE: extension/servletannotations/src/main/java/cloud/piranha/extension/servletannotations/ServletAnnotationsExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.servletannotations; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import jakarta.servlet.ServletContainerInitializer; import java.lang.System.Logger; import java.lang.System.Logger.Level; import java.lang.reflect.InvocationTargetException; /** * The WebApplicationExtension for Servlet annotation processing. * * @author Thiago Henrique Hupner * @author Manfred Riem (mriem@manorrock.com) */ public class ServletAnnotationsExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(ServletAnnotationsExtension.class.getName()); /** * Constructor. */ public ServletAnnotationsExtension() { } @Override public void configure(WebApplication webApplication) { try { ClassLoader classLoader = webApplication.getClassLoader(); Class clazz = classLoader .loadClass(ServletAnnotationsInitializer.class.getName()) .asSubclass(ServletContainerInitializer.class); ServletContainerInitializer initializer = clazz.getDeclaredConstructor().newInstance(); webApplication.addInitializer(initializer); } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { LOGGER.log(Level.WARNING, "Unable to enable the WebAnnotationExtension", ex); } } } ================================================ FILE: extension/servletannotations/src/main/java/cloud/piranha/extension/servletannotations/ServletAnnotationsInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.servletannotations; import cloud.piranha.core.api.AnnotationInfo; import cloud.piranha.core.api.AnnotationManager; import cloud.piranha.core.api.WebApplication; import jakarta.annotation.security.DeclareRoles; import jakarta.annotation.security.RolesAllowed; import jakarta.servlet.DispatcherType; import jakarta.servlet.FilterRegistration; import jakarta.servlet.MultipartConfigElement; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRegistration; import jakarta.servlet.ServletRegistration.Dynamic; import jakarta.servlet.annotation.MultipartConfig; import jakarta.servlet.annotation.ServletSecurity; import jakarta.servlet.annotation.WebFilter; import jakarta.servlet.annotation.WebListener; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import java.lang.System.Logger; import static java.lang.System.Logger.Level.WARNING; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import static java.util.Arrays.asList; import static java.util.Arrays.stream; import static java.util.EnumSet.noneOf; import java.util.EventListener; import java.util.List; import java.util.Map.Entry; import java.util.Set; import static java.util.stream.Collectors.toCollection; /** * The standard Servlet annotations initializer. * * @author Arjan Tijms * @author Manfred Riem (mriem@manorrock.com) */ public class ServletAnnotationsInitializer implements ServletContainerInitializer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(ServletAnnotationsInitializer.class.getName()); /** * Constructor. */ public ServletAnnotationsInitializer() { } @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { WebApplication webApp = (WebApplication) servletContext; if (!webApp.isMetadataComplete()) { AnnotationManager annotationManager = webApp.getManager().getAnnotationManager(); // Process @WebServlet for (AnnotationInfo annotationInfo : annotationManager.getAnnotations(WebServlet.class)) { WebServlet webServlet = annotationInfo.getInstance(); String servletName = webServlet.name(); if ("".equals(servletName)) { servletName = annotationInfo.getTargetType().getName(); // WebServlet only has target Type } // Add the Servlet ServletRegistration registration = webApp.addServlet(servletName, annotationInfo.getTargetType().getName()); if (registration == null) { registration = webApp.getServletRegistration(servletName); } // Add params if (webServlet.initParams().length != 0) { final ServletRegistration finalServletRegistration = registration; stream(webServlet.initParams()).forEach( initParam -> finalServletRegistration.setInitParameter(initParam.name(), initParam.value())); } if (registration instanceof Dynamic dynamic) { dynamic.setAsyncSupported(webServlet.asyncSupported()); } String[] urlPatterns = webServlet.value(); if (urlPatterns.length == 0) { urlPatterns = webServlet.urlPatterns(); } // Add mapping if (registration != null) { registration.addMapping(urlPatterns); } } // Process @WebFilter for (AnnotationInfo annotationInfo : annotationManager.getAnnotations(WebFilter.class)) { WebFilter webFilter = annotationInfo.getInstance(); String filterName = webFilter.filterName(); if ("".equals(filterName)) { filterName = annotationInfo.getTargetType().getName(); // WebServlet only has target Type } // Add the Filter FilterRegistration.Dynamic registration = webApp.addFilter(filterName, annotationInfo.getTargetType().getName()); // Add params if (webFilter.initParams().length != 0) { stream(webFilter.initParams()).forEach(initParam -> registration.setInitParameter(initParam.name(), initParam.value()) ); } if (registration != null) { registration.setAsyncSupported(webFilter.asyncSupported()); } String[] urlPatterns = webFilter.value(); if (urlPatterns.length == 0) { urlPatterns = webFilter.urlPatterns(); } // Add mapping for URL patterns, if any if (registration != null && urlPatterns.length > 0) { registration.addMappingForUrlPatterns(null, false, urlPatterns); } // Add mapping for Servlet names, if any if (webFilter.servletNames().length > 0) { registration.addMappingForServletNames( stream(webFilter.dispatcherTypes()) .collect(toCollection(() -> noneOf(DispatcherType.class))), true, webFilter.servletNames()); } } // Process @MultipartConfig // This assumes all applicable Servlets have been registered, either via web.xml, annotations or programmatically prior to this point. for (AnnotationInfo annotationInfo : annotationManager.getAnnotations(MultipartConfig.class)) { for (ServletRegistration servletRegistration : servletContext.getServletRegistrations().values()) { if (servletRegistration instanceof Dynamic dynamicRegistration) { Class targetType = annotationInfo.getTargetType(); if (targetType != null && targetType.getName().equals(servletRegistration.getClassName())) { dynamicRegistration.setMultipartConfig(new MultipartConfigElement(annotationInfo.getInstance())); } } } } // Process @ServletSecurity List, ServletSecurity>> securityAnnotations = new ArrayList<>(); for (AnnotationInfo annotationInfo : annotationManager.getAnnotations(ServletSecurity.class)) { Class servlet = getTargetServlet(annotationInfo); // Take into account mixed mapped (annotation + web.xml later) WebServlet webServlet = servlet.getAnnotation(WebServlet.class); if (webServlet != null) { String[] urlPatterns = webServlet.value(); if (urlPatterns.length == 0) { urlPatterns = webServlet.urlPatterns(); } securityAnnotations.add(new SimpleImmutableEntry<>( asList(urlPatterns), annotationInfo.getInstance())); } else { LOGGER.log(WARNING, "@ServletSecurity encountered on Servlet " + servlet + "but no @WebServlet encountered"); } } webApp.setAttribute( "cloud.piranha.authorization.exousia.AuthorizationPreInitializer.security.annotations", securityAnnotations ); // Collect the roles from various annotations for (AnnotationInfo rolesAllowedInfo : annotationManager.getAnnotations(RolesAllowed.class)) { webApp.declareRoles(rolesAllowedInfo.getInstance().value()); } for (AnnotationInfo declareRolesInfo : annotationManager.getAnnotations(DeclareRoles.class)) { webApp.declareRoles(declareRolesInfo.getInstance().value()); } // Process @WebListener for (AnnotationInfo annotationInfo : annotationManager.getAnnotations(WebListener.class)) { webApp.addListener(getTargetListener(annotationInfo)); } } } @SuppressWarnings("unchecked") private Class getTargetListener(AnnotationInfo annotationInfo) { return (Class) annotationInfo.getTargetType(); } @SuppressWarnings("unchecked") private Class getTargetServlet(AnnotationInfo annotationInfo) { return (Class) annotationInfo.getTargetType(); } } ================================================ FILE: extension/servletannotations/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Servlet annotations integration extension. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.servletannotations { exports cloud.piranha.extension.servletannotations; opens cloud.piranha.extension.servletannotations; requires transitive cloud.piranha.core.api; requires jakarta.annotation; } ================================================ FILE: extension/servletannotations/src/test/java/cloud/piranha/extension/servletannotations/ServletAnnotationsExtensionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.servletannotations; import cloud.piranha.core.api.FilterEnvironment; import cloud.piranha.core.api.ServletEnvironment; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.core.impl.DefaultWebApplicationClassLoader; import cloud.piranha.core.impl.DefaultWebApplicationExtensionContext; import cloud.piranha.extension.annotationscan.internal.InternalAnnotationScanAnnotationManager; import cloud.piranha.extension.annotationscan.AnnotationScanInitializer; import cloud.piranha.resource.impl.ClassResource; import jakarta.servlet.FilterRegistration; import jakarta.servlet.ServletRegistration; import java.io.File; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.Test; /** * The JUnit tests for the WebXmlExtension class. * * @author Manfred Riem (mriem@manorrock.com) */ class ServletAnnotationsExtensionTest { /** * Test onStartup method. * * @throws Exception when a serious error occurs. */ @Test void testOnStartup() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.getManager().setAnnotationManager(new InternalAnnotationScanAnnotationManager()); webApplication.addInitializer(new AnnotationScanInitializer()); DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(new File("")); classLoader.getResourceManager().addResource(new ClassResource(TestServlet.class.getName())); webApplication.setClassLoader(classLoader); DefaultWebApplicationExtensionContext context = new DefaultWebApplicationExtensionContext(); context.add(ServletAnnotationsExtension.class); context.configure(webApplication); webApplication.initialize(); ServletRegistration registration = webApplication.getServletRegistration("TestServlet"); assertNotNull(registration); } /** * Test onStartup method. * * @throws Exception when a serious error occurs. */ @Test void testOnStartup2() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.getManager().setAnnotationManager(new InternalAnnotationScanAnnotationManager()); webApplication.addInitializer(new AnnotationScanInitializer()); DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(new File("")); classLoader.getResourceManager().addResource(new ClassResource(Test2Servlet.class.getName())); webApplication.setClassLoader(classLoader); DefaultWebApplicationExtensionContext context = new DefaultWebApplicationExtensionContext(); context.add(ServletAnnotationsExtension.class); context.configure(webApplication); webApplication.initialize(); ServletRegistration registration = webApplication.getServletRegistration(Test2Servlet.class.getName()); assertNotNull(registration); } /** * Test onStartup method. * * @throws Exception when a serious error occurs. */ @Test void testOnStartup3() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.getManager().setAnnotationManager(new InternalAnnotationScanAnnotationManager()); webApplication.addInitializer(new AnnotationScanInitializer()); DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(new File("")); classLoader.getResourceManager().addResource(new ClassResource(Test3Servlet.class.getName())); webApplication.setClassLoader(classLoader); DefaultWebApplicationExtensionContext context = new DefaultWebApplicationExtensionContext(); context.add(ServletAnnotationsExtension.class); context.configure(webApplication); webApplication.initialize(); ServletRegistration registration = webApplication.getServletRegistration("Test3Servlet"); assertNotNull(registration); assertFalse(registration.getMappings().isEmpty()); } /** * Test onStartup method. * * @throws Exception when a serious error occurs. */ @Test void testOnStartup4() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.getManager().setAnnotationManager(new InternalAnnotationScanAnnotationManager()); webApplication.addInitializer(new AnnotationScanInitializer()); DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(new File("")); classLoader.getResourceManager().addResource(new ClassResource(Test4Servlet.class.getName())); webApplication.setClassLoader(classLoader); DefaultWebApplicationExtensionContext context = new DefaultWebApplicationExtensionContext(); context.add(ServletAnnotationsExtension.class); context.configure(webApplication); webApplication.initialize(); ServletRegistration registration = webApplication.getServletRegistration("Test4Servlet"); assertNotNull(registration); ServletEnvironment servletEnvironment = (ServletEnvironment) registration; assertEquals("value", servletEnvironment.getInitParameter("name")); } /** * Test onStartup method. * * @throws Exception when a serious error occurs. */ @Test void testOnStartup5() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.getManager().setAnnotationManager(new InternalAnnotationScanAnnotationManager()); webApplication.addInitializer(new AnnotationScanInitializer()); DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(new File("")); classLoader.getResourceManager().addResource(new ClassResource(TestFilter.class.getName())); webApplication.setClassLoader(classLoader); DefaultWebApplicationExtensionContext context = new DefaultWebApplicationExtensionContext(); context.add(ServletAnnotationsExtension.class); context.configure(webApplication); webApplication.initialize(); FilterRegistration registration = webApplication.getFilterRegistration("TestFilter"); assertNotNull(registration); } /** * Test onStartup method. * * @throws Exception when a serious error occurs. */ @Test void testOnStartup6() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.getManager().setAnnotationManager(new InternalAnnotationScanAnnotationManager()); webApplication.addInitializer(new AnnotationScanInitializer()); DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(new File("")); classLoader.getResourceManager().addResource(new ClassResource(Test2Filter.class.getName())); webApplication.setClassLoader(classLoader); DefaultWebApplicationExtensionContext context = new DefaultWebApplicationExtensionContext(); context.add(ServletAnnotationsExtension.class); context.configure(webApplication); webApplication.initialize(); FilterRegistration registration = webApplication.getFilterRegistration(Test2Filter.class.getName()); assertNotNull(registration); } /** * Test onStartup method. * * @throws Exception when a serious error occurs. */ @Test void testOnStartup7() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.getManager().setAnnotationManager(new InternalAnnotationScanAnnotationManager()); webApplication.addInitializer(new AnnotationScanInitializer()); DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(new File("")); classLoader.getResourceManager().addResource(new ClassResource(Test3Filter.class.getName())); webApplication.setClassLoader(classLoader); DefaultWebApplicationExtensionContext context = new DefaultWebApplicationExtensionContext(); context.add(ServletAnnotationsExtension.class); context.configure(webApplication); webApplication.initialize(); FilterRegistration registration = webApplication.getFilterRegistration("Test3Filter"); assertNotNull(registration); FilterEnvironment filterEnvironment = (FilterEnvironment) registration; assertEquals("value", filterEnvironment.getInitParameter("name")); } /** * Test onStartup method. * * @throws Exception when a serious error occurs. */ @Test void testOnStartup8() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.getManager().setAnnotationManager(new InternalAnnotationScanAnnotationManager()); webApplication.addInitializer(new AnnotationScanInitializer()); DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(new File("")); classLoader.getResourceManager().addResource(new ClassResource(Test4Filter.class.getName())); webApplication.setClassLoader(classLoader); DefaultWebApplicationExtensionContext context = new DefaultWebApplicationExtensionContext(); context.add(ServletAnnotationsExtension.class); context.configure(webApplication); webApplication.initialize(); FilterRegistration registration = webApplication.getFilterRegistration("Test4Filter"); assertNotNull(registration); assertFalse(registration.getUrlPatternMappings().isEmpty()); } /** * Test onStartup method. * * @throws Exception when a serious error occurs. */ @Test void testOnStartup9() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.getManager().setAnnotationManager(new InternalAnnotationScanAnnotationManager()); webApplication.addInitializer(new AnnotationScanInitializer()); DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(new File("")); classLoader.getResourceManager().addResource(new ClassResource(Test5Servlet.class.getName())); webApplication.setClassLoader(classLoader); DefaultWebApplicationExtensionContext context = new DefaultWebApplicationExtensionContext(); context.add(ServletAnnotationsExtension.class); context.configure(webApplication); webApplication.initialize(); ServletRegistration registration = webApplication.getServletRegistration("Test5Servlet"); assertNotNull(registration); ServletEnvironment servletEnvironment = (ServletEnvironment) registration; assertNotNull(servletEnvironment.getMultipartConfig()); } /** * Test onStartup method. * * @throws Exception when a serious error occurs. */ @Test void testOnStartup10() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.getManager().setAnnotationManager(new InternalAnnotationScanAnnotationManager()); webApplication.addInitializer(new AnnotationScanInitializer()); DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(new File("")); classLoader.getResourceManager().addResource(new ClassResource(TestListener.class.getName())); webApplication.setClassLoader(classLoader); DefaultWebApplicationExtensionContext context = new DefaultWebApplicationExtensionContext(); context.add(ServletAnnotationsExtension.class); context.configure(webApplication); webApplication.initialize(); assertNotNull(webApplication.getAttribute("listenerAdded")); } } ================================================ FILE: extension/servletannotations/src/test/java/cloud/piranha/extension/servletannotations/Test2Filter.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.servletannotations; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.annotation.WebFilter; import java.io.IOException; /** * The test Filter. * * @author Manfred Riem (mriem@manorrock.com) */ @WebFilter(value = {"/url1", "/url2/*", "*.url3"}) public class Test2Filter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { } } ================================================ FILE: extension/servletannotations/src/test/java/cloud/piranha/extension/servletannotations/Test2Servlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.servletannotations; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * The test Servlet. * * @author Manfred Riem (mriem@manorrock.com) */ @WebServlet(value = {"/url1", "/url2/*", "*.url3"}) public class Test2Servlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { PrintWriter writer = response.getWriter(); writer.println("Hurray, it worked!"); } } ================================================ FILE: extension/servletannotations/src/test/java/cloud/piranha/extension/servletannotations/Test3Filter.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.servletannotations; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.annotation.WebFilter; import jakarta.servlet.annotation.WebInitParam; import java.io.IOException; /** * The test Filter. * * @author Manfred Riem (mriem@manorrock.com) */ @WebFilter(filterName = "Test3Filter", value = {"/url1", "/url2/*", "*.url3"}, initParams = { @WebInitParam(name = "name", value = "value") }) public class Test3Filter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { } } ================================================ FILE: extension/servletannotations/src/test/java/cloud/piranha/extension/servletannotations/Test3Servlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.servletannotations; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * The test Servlet. * * @author Manfred Riem (mriem@manorrock.com) */ @WebServlet(name = "Test3Servlet", urlPatterns = {"/url1", "/url2/*", "*.url3"}) public class Test3Servlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { PrintWriter writer = response.getWriter(); writer.println("Hurray, it worked!"); } } ================================================ FILE: extension/servletannotations/src/test/java/cloud/piranha/extension/servletannotations/Test4Filter.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.servletannotations; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.annotation.WebFilter; import java.io.IOException; /** * The test Filter. * * @author Manfred Riem (mriem@manorrock.com) */ @WebFilter(filterName = "Test4Filter", urlPatterns = {"/url1", "/url2/*", "*.url3"}) public class Test4Filter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { } } ================================================ FILE: extension/servletannotations/src/test/java/cloud/piranha/extension/servletannotations/Test4Servlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.servletannotations; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebInitParam; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * The test Servlet. * * @author Manfred Riem (mriem@manorrock.com) */ @WebServlet(name = "Test4Servlet", urlPatterns = {"/url1", "/url2/*", "*.url3"}, initParams = { @WebInitParam(name = "name", value = "value") }) public class Test4Servlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { PrintWriter writer = response.getWriter(); writer.println("Hurray, it worked!"); } } ================================================ FILE: extension/servletannotations/src/test/java/cloud/piranha/extension/servletannotations/Test5Servlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.servletannotations; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.MultipartConfig; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * The test Servlet. * * @author Manfred Riem (mriem@manorrock.com) */ @WebServlet(name = "Test5Servlet", urlPatterns = {"/url1", "/url2/*", "*.url3"}) @MultipartConfig(maxFileSize = 1024) public class Test5Servlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { PrintWriter writer = response.getWriter(); writer.println("Hurray, it worked!"); } } ================================================ FILE: extension/servletannotations/src/test/java/cloud/piranha/extension/servletannotations/TestFilter.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.servletannotations; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.annotation.WebFilter; import java.io.IOException; /** * The test Filter. * * @author Manfred Riem (mriem@manorrock.com) */ @WebFilter(filterName = "TestFilter", value = {"/url1", "/url2/*", "*.url3"}) public class TestFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { } } ================================================ FILE: extension/servletannotations/src/test/java/cloud/piranha/extension/servletannotations/TestListener.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.servletannotations; import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletContextListener; import jakarta.servlet.annotation.WebListener; /** * A test ServletContextListener. * * @author Manfred Riem (mriem@manorrock.com) */ @WebListener public class TestListener implements ServletContextListener { @Override public void contextDestroyed(ServletContextEvent event) { } @Override public void contextInitialized(ServletContextEvent event) { event.getServletContext().setAttribute("listenerAdded", true); } } ================================================ FILE: extension/servletannotations/src/test/java/cloud/piranha/extension/servletannotations/TestServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.servletannotations; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * The test Servlet. * * @author Manfred Riem (mriem@manorrock.com) */ @WebServlet(name = "TestServlet", value = {"/url1", "/url2/*", "*.url3"}) public class TestServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { PrintWriter writer = response.getWriter(); writer.println("Hurray, it worked!"); } } ================================================ FILE: extension/servletannotations/src/test/java/cloud/piranha/extension/servletannotations/WebServletTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.servletannotations; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.core.impl.DefaultWebApplicationClassLoader; import cloud.piranha.embedded.EmbeddedRequest; import cloud.piranha.embedded.EmbeddedRequestBuilder; import cloud.piranha.embedded.EmbeddedResponse; import cloud.piranha.embedded.EmbeddedResponseBuilder; import cloud.piranha.extension.annotationscan.AnnotationScanInitializer; import cloud.piranha.extension.annotationscan.internal.InternalAnnotationScanAnnotationManager; import cloud.piranha.extension.webxml.WebXmlInitializer; import cloud.piranha.resource.impl.ClassResource; import cloud.piranha.resource.impl.DirectoryResource; import java.io.File; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the @WebServlet annotation. * * @author Manfred Riem (mriem@manorrock.com) */ class WebServletTest { /** * Test @WebServlet annotation. * * @throws Exception when a serious error occurs. */ @Test void testWebServletUrl1() throws Exception { DefaultWebApplication application = new DefaultWebApplication(); application.getManager().setAnnotationManager(new InternalAnnotationScanAnnotationManager()); application.addResource(new DirectoryResource("src/test/webapp/webservlet")); DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(new File("src/test/webapp/webservlet")); classLoader.getResourceManager().addResource(new ClassResource(TestServlet.class.getName())); application.setClassLoader(classLoader); application.addInitializer(new AnnotationScanInitializer()); application.addInitializer(new WebXmlInitializer()); application.addInitializer(new ServletAnnotationsInitializer()); application.initialize(); application.start(); EmbeddedRequest request = new EmbeddedRequestBuilder().servletPath("/url1").build(); request.setWebApplication(application); EmbeddedResponse response = new EmbeddedResponseBuilder().bodyOnly(true).build(); response.setWebApplication(application); application.service(request, response); application.stop(); assertTrue(response.getResponseAsString().contains("Hurray, it worked!")); } /** * Test @WebServlet annotation. * * @throws Exception when a serious error occurs. */ @Test void testWebServletUrl1b() throws Exception { DefaultWebApplication application = new DefaultWebApplication(); application.getManager().setAnnotationManager(new InternalAnnotationScanAnnotationManager()); application.addResource(new DirectoryResource("src/test/webapp/webservlet")); DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(new File("src/test/webapp/webservlet")); classLoader.getResourceManager().addResource(new ClassResource(TestServlet.class.getName())); application.setClassLoader(classLoader); application.addInitializer(new AnnotationScanInitializer()); application.addInitializer(new WebXmlInitializer()); application.addInitializer(new ServletAnnotationsInitializer()); application.initialize(); application.start(); EmbeddedRequest request = new EmbeddedRequestBuilder().servletPath("/url1b").build(); request.setWebApplication(application); EmbeddedResponse response = new EmbeddedResponseBuilder().bodyOnly(true).build(); response.setWebApplication(application); application.service(request, response); application.stop(); assertTrue(response.getResponseAsString().contains("Hurray, it worked!")); } /** * Test @WebServlet annotation. * * @throws Exception when a serious error occurs. */ @Test void testWebServletUrl2() throws Exception { DefaultWebApplication application = new DefaultWebApplication(); application.getManager().setAnnotationManager(new InternalAnnotationScanAnnotationManager()); application.addResource(new DirectoryResource("src/test/webapp/webservlet")); DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(new File("src/test/webapp/webservlet")); classLoader.getResourceManager().addResource(new ClassResource(TestServlet.class.getName())); application.setClassLoader(classLoader); application.addInitializer(new AnnotationScanInitializer()); application.addInitializer(new WebXmlInitializer()); application.addInitializer(new ServletAnnotationsInitializer()); application.initialize(); application.start(); EmbeddedRequest request = new EmbeddedRequestBuilder().servletPath("/url2/test").build(); request.setWebApplication(application); EmbeddedResponse response = new EmbeddedResponseBuilder().bodyOnly(true).build(); response.setWebApplication(application); application.service(request, response); application.stop(); assertTrue(response.getResponseAsString().contains("Hurray, it worked!")); } /** * Test @WebServlet annotation. * * @throws Exception when a serious error occurs. */ @Test void testWebServletUrl2b() throws Exception { DefaultWebApplication application = new DefaultWebApplication(); application.getManager().setAnnotationManager(new InternalAnnotationScanAnnotationManager()); application.addResource(new DirectoryResource("src/test/webapp/webservlet")); DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(new File("src/test/webapp/webservlet")); classLoader.getResourceManager().addResource(new ClassResource(TestServlet.class.getName())); application.setClassLoader(classLoader); application.addInitializer(new AnnotationScanInitializer()); application.addInitializer(new WebXmlInitializer()); application.addInitializer(new ServletAnnotationsInitializer()); application.initialize(); application.start(); EmbeddedRequest request = new EmbeddedRequestBuilder().servletPath("/url2b/test").build(); request.setWebApplication(application); EmbeddedResponse response = new EmbeddedResponseBuilder().bodyOnly(true).build(); response.setWebApplication(application); application.service(request, response); application.stop(); assertTrue(response.getResponseAsString().contains("Hurray, it worked!")); } /** * Test @WebServlet annotation. * * @throws Exception when a serious error occurs. */ @Test void testWebServletUrl3() throws Exception { DefaultWebApplication application = new DefaultWebApplication(); application.getManager().setAnnotationManager(new InternalAnnotationScanAnnotationManager()); application.addResource(new DirectoryResource("src/test/webapp/webservlet")); DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(new File("src/test/webapp/webservlet")); classLoader.getResourceManager().addResource(new ClassResource(TestServlet.class.getName())); application.setClassLoader(classLoader); application.addInitializer(new AnnotationScanInitializer()); application.addInitializer(new WebXmlInitializer()); application.addInitializer(new ServletAnnotationsInitializer()); application.initialize(); application.start(); EmbeddedRequest request = new EmbeddedRequestBuilder().servletPath("/my/extension/test.url3").build(); request.setWebApplication(application); EmbeddedResponse response = new EmbeddedResponseBuilder().bodyOnly(true).build(); response.setWebApplication(application); application.service(request, response); application.stop(); assertTrue(response.getResponseAsString().contains("Hurray, it worked!")); } /** * Test @WebServlet annotation. * * @throws Exception when a serious error occurs. */ @Test void testWebServletUrl3b() throws Exception { DefaultWebApplication application = new DefaultWebApplication(); application.getManager().setAnnotationManager(new InternalAnnotationScanAnnotationManager()); application.addResource(new DirectoryResource("src/test/webapp/webservlet")); DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(new File("src/test/webapp/webservlet")); classLoader.getResourceManager().addResource(new ClassResource(TestServlet.class.getName())); application.setClassLoader(classLoader); application.addInitializer(new AnnotationScanInitializer()); application.addInitializer(new WebXmlInitializer()); application.addInitializer(new ServletAnnotationsInitializer()); application.initialize(); application.start(); EmbeddedRequest request = new EmbeddedRequestBuilder().servletPath("/my/extension/test.url3b").build(); request.setWebApplication(application); EmbeddedResponse response = new EmbeddedResponseBuilder().bodyOnly(true).build(); response.setWebApplication(application); application.service(request, response); application.stop(); assertTrue(response.getResponseAsString().contains("Hurray, it worked!")); } } ================================================ FILE: extension/servletannotations/src/test/webapp/webservlet/WEB-INF/web.xml ================================================ TestServlet cloud.piranha.extension.servletannotations.TestServlet TestServlet /url1b /url2b/* *.url3b ================================================ FILE: extension/soteria/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-soteria jar Piranha - Extension - Soteria cloud.piranha.core piranha-core-impl ${project.version} compile com.nimbusds nimbus-jose-jwt compile jakarta.authentication jakarta.authentication-api compile jakarta.enterprise jakarta.enterprise.cdi-api compile org.glassfish.soteria soteria.spi.bean.decorator.weld compile org.glassfish.soteria soteria compile jakarta.platform jakarta.jakartaee-web-api test cloud.piranha.extension piranha-extension-herring ${project.version} test cloud.piranha.extension piranha-extension-security-servlet ${project.version} test cloud.piranha.extension piranha-extension-scinitializer ${project.version} test cloud.piranha.extension piranha-extension-weld ${project.version} test org.junit.jupiter junit-jupiter-api 5.10.3 test org.junit.jupiter junit-jupiter-params 5.10.3 test org.junit.jupiter junit-jupiter-engine 5.10.3 test org.apache.maven.plugins maven-surefire-plugin 1 false false ================================================ FILE: extension/soteria/src/main/java/cloud/piranha/extension/soteria/IdentityStoreLoginHandler.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.soteria; import static jakarta.security.enterprise.identitystore.CredentialValidationResult.Status.VALID; import jakarta.enterprise.inject.spi.CDI; import jakarta.security.enterprise.credential.Password; import jakarta.security.enterprise.credential.UsernamePasswordCredential; import jakarta.security.enterprise.identitystore.CredentialValidationResult; import jakarta.security.enterprise.identitystore.IdentityStoreHandler; import jakarta.servlet.http.HttpServletRequest; import cloud.piranha.core.impl.DefaultAuthenticatedIdentity; import cloud.piranha.core.api.AuthenticatedIdentity; import cloud.piranha.core.api.SecurityManager.UsernamePasswordLoginHandler; /** * The IdentityStore username/password login handler. * * @author Arjan Tijms * @author Manfred Riem (mriem@manorrock.com) */ public class IdentityStoreLoginHandler implements UsernamePasswordLoginHandler { /** * Constructor. */ public IdentityStoreLoginHandler() { } @Override public AuthenticatedIdentity login(HttpServletRequest request, String username, String password) { CredentialValidationResult result = CDI.current() .select(IdentityStoreHandler.class) .get() .validate(new UsernamePasswordCredential(username, new Password(password))); if (result.getStatus() == VALID) { return new DefaultAuthenticatedIdentity(result.getCallerPrincipal(), result.getCallerGroups()); } return null; } } ================================================ FILE: extension/soteria/src/main/java/cloud/piranha/extension/soteria/SoteriaInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.soteria; import static java.lang.System.Logger.Level.DEBUG; import java.lang.System.Logger; import java.util.Set; import org.glassfish.soteria.servlet.SamRegistrationInstaller; import cloud.piranha.core.api.WebApplication; import jakarta.enterprise.inject.spi.CDI; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; /** * The Soteria initializer. * * @author Arjan Tijms * @author Manfred Riem (mriem@manorrock.com) */ public class SoteriaInitializer implements ServletContainerInitializer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(SoteriaInitializer.class.getName()); /** * Constructor. */ public SoteriaInitializer() { } /** * Initialize Soteria. * * @param classes the classes. * @param servletContext the Servlet context. * @throws ServletException when a Servlet error occurs. */ @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { LOGGER.log(DEBUG, "Initializing Soteria"); if (!isCDIEnabled()) { return; } WebApplication webApplication = (WebApplication) servletContext; webApplication.getManager().getSecurityManager().setUsernamePasswordLoginHandler(new IdentityStoreLoginHandler()); SamRegistrationInstaller installer = new SamRegistrationInstaller(); installer.onStartup(classes, servletContext); LOGGER.log(DEBUG, "Initialized Soteria"); } private boolean isCDIEnabled() { try { CDI.current(); return true; } catch (IllegalStateException e) { return false; } } } ================================================ FILE: extension/soteria/src/main/java/cloud/piranha/extension/soteria/SoteriaPreCDIInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.soteria; import cloud.piranha.core.api.SecurityManager; import cloud.piranha.core.api.WebApplication; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import java.lang.System.Logger; import static java.lang.System.Logger.Level.DEBUG; import java.util.Set; import org.glassfish.soteria.SoteriaServiceProviders; import org.glassfish.soteria.cdi.spi.WebXmlLoginConfig; /** * The Soteria initializer. * * @author Arjan Tijms * @author Manfred Riem (mriem@manorrock.com) */ public class SoteriaPreCDIInitializer implements ServletContainerInitializer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(SoteriaPreCDIInitializer.class.getName()); /** * Constructor. */ public SoteriaPreCDIInitializer() { } /** * Initialize Soteria. * * @param classes the classes. * @param servletContext the Servlet context. * @throws ServletException when a Servlet error occurs. */ @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { WebApplication webApplication = (WebApplication) servletContext; SecurityManager manager = webApplication.getManager().getSecurityManager(); if (manager.getAuthMethod() != null) { LOGGER.log(DEBUG, "AuthMethod {0} configured in web.xml and handled by Soteria.", manager.getAuthMethod()); WebXmlLoginConfig webXmlLoginConfig = SoteriaServiceProviders .getServiceProvider(WebXmlLoginConfig.class); webXmlLoginConfig.setAuthMethod(manager.getAuthMethod()); webXmlLoginConfig.setFormErrorPage(manager.getFormErrorPage()); webXmlLoginConfig.setFormLoginPage(manager.getFormLoginPage()); webXmlLoginConfig.setRealmName(manager.getRealmName()); } } } ================================================ FILE: extension/soteria/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module integrates Soteria into Piranha. See * https://github.com/eclipse-ee4j/soteria for more information. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.soteria { exports cloud.piranha.extension.soteria; opens cloud.piranha.extension.soteria; requires transitive cloud.piranha.core.api; requires cloud.piranha.core.impl; requires jakarta.cdi; requires jakarta.inject; requires jakarta.security; requires transitive org.glassfish.soteria; } ================================================ FILE: extension/soteria/src/test/java/cloud/piranha/extension/soteria/IdentityStoreLoginHandlerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.soteria; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.core.impl.DefaultWebApplicationExtensionContext; import cloud.piranha.core.impl.DefaultWebApplicationRequest; import cloud.piranha.extension.herring.HerringExtension; import cloud.piranha.extension.herring.HerringInitialContextFactory; import cloud.piranha.extension.scinitializer.ServletContainerInitializerExtension; import cloud.piranha.extension.weld.WeldExtension; import cloud.piranha.resource.impl.AliasedDirectoryResource; import cloud.piranha.resource.impl.DirectoryResource; import java.io.File; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static javax.naming.Context.INITIAL_CONTEXT_FACTORY; /** * The JUnit test for the IdentityStoreLoginHandler class. * * @author Manfred Riem (mriem@manorrock.com) */ public class IdentityStoreLoginHandlerTest { /** * Test login method with correct username / password. */ @Test public void testLoginWithCorrectUsernameAndPassword() { /* * Setup web application with JNDI and Weld. */ System.getProperties().put(INITIAL_CONTEXT_FACTORY, HerringInitialContextFactory.class.getName()); DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource("src/test/webapp")); webApplication.addResource(new AliasedDirectoryResource( new File("target/test-classes"), "/WEB-INF/classes")); DefaultWebApplicationExtensionContext context = new DefaultWebApplicationExtensionContext(); context.add(HerringExtension.class); context.add(WeldExtension.class); context.add(ServletContainerInitializerExtension.class); context.configure(webApplication); webApplication.initialize(); webApplication.start(); /* * Validate the test_user / test_password are correct. */ DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); String username = "test_user"; String password = "test_password"; IdentityStoreLoginHandler handler = new IdentityStoreLoginHandler(); assertNotNull(handler.login(request, username, password)); /* * Shutdown and cleanup. */ webApplication.stop(); System.getProperties().remove(INITIAL_CONTEXT_FACTORY); } /** * Test login method with incorrect username / password. */ @Test public void testLoginWithIncorrectUsernameAndPassword() { /* * Setup web application with JNDI and Weld. */ System.getProperties().put(INITIAL_CONTEXT_FACTORY, HerringInitialContextFactory.class.getName()); DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource("src/test/webapp")); webApplication.addResource(new AliasedDirectoryResource( new File("target/test-classes"), "/WEB-INF/classes")); DefaultWebApplicationExtensionContext context = new DefaultWebApplicationExtensionContext(); context.add(HerringExtension.class); context.add(WeldExtension.class); context.add(ServletContainerInitializerExtension.class); context.configure(webApplication); webApplication.initialize(); webApplication.start(); /* * Validate the invalid_user / invalid_password are incorrect. */ DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); String username = "invalid_user"; String password = "invalid_password"; IdentityStoreLoginHandler handler = new IdentityStoreLoginHandler(); assertNull(handler.login(request, username, password)); /* * Shutdown and cleanup. */ webApplication.stop(); System.getProperties().remove(INITIAL_CONTEXT_FACTORY); } } ================================================ FILE: extension/soteria/src/test/java/cloud/piranha/extension/soteria/SoteriaInitializerNoCdiTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.soteria; import cloud.piranha.core.impl.DefaultWebApplication; import static org.junit.jupiter.api.Assertions.assertNull; import org.junit.jupiter.api.Test; /** * The JUnit tests for the SoteriaInitializer class. * * @author Manfred Riem (mriem@manorrock.com) */ public class SoteriaInitializerNoCdiTest { /** * Test onStartup method. */ @Test public void testOnStartup() throws Exception { /** * Setup web application without CDI */ DefaultWebApplication webApplication = new DefaultWebApplication(); /* * Initializer is still called but nothing is setup. */ SoteriaInitializer initializer = new SoteriaInitializer(); initializer.onStartup(null, webApplication); /* * No UsernamePasswordLoginHandler should be set. */ assertNull(webApplication.getManager().getSecurityManager().getUsernamePasswordLoginHandler()); } } ================================================ FILE: extension/soteria/src/test/java/cloud/piranha/extension/soteria/SoteriaInitializerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.soteria; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.core.impl.DefaultWebApplicationExtensionContext; import cloud.piranha.extension.herring.HerringExtension; import cloud.piranha.extension.herring.HerringInitialContextFactory; import cloud.piranha.extension.scinitializer.ServletContainerInitializerExtension; import cloud.piranha.extension.weld.WeldExtension; import cloud.piranha.resource.impl.AliasedDirectoryResource; import cloud.piranha.resource.impl.DirectoryResource; import java.io.File; import static javax.naming.Context.INITIAL_CONTEXT_FACTORY; import org.junit.jupiter.api.Test; /** * The JUnit tests for the SoteriaInitializer class. * * @author Manfred Riem (mriem@manorrock.com) */ public class SoteriaInitializerTest { /** * Test onStartup method. */ @Test public void testOnStartup() throws Exception { /** * Setup web application without CDI */ DefaultWebApplication webApplication = new DefaultWebApplication(); /* * Initializer is still called but nothing is setup. */ SoteriaInitializer initializer = new SoteriaInitializer(); initializer.onStartup(null, webApplication); } /** * Test onStartup method. */ @Test public void testOnStartup2() throws Exception { /* * Setup web application with JNDI and Weld. */ System.getProperties().put(INITIAL_CONTEXT_FACTORY, HerringInitialContextFactory.class.getName()); DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource("src/test/webapp")); webApplication.addResource(new AliasedDirectoryResource( new File("target/test-classes"), "/WEB-INF/classes")); DefaultWebApplicationExtensionContext context = new DefaultWebApplicationExtensionContext(); context.add(HerringExtension.class); context.add(WeldExtension.class); context.add(ServletContainerInitializerExtension.class); context.configure(webApplication); /* * Add SoteriaInitializer. */ webApplication.addInitializer(SoteriaInitializer.class.getName()); webApplication.initialize(); webApplication.start(); } } ================================================ FILE: extension/soteria/src/test/java/cloud/piranha/extension/soteria/SoteriaPreCDIInitializerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.soteria; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.extension.security.servlet.ServletSecurityManager; import org.glassfish.soteria.SoteriaServiceProviders; import org.glassfish.soteria.cdi.spi.WebXmlLoginConfig; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; /** * The JUnit tests for the SoteriaPreCDIInitializer class. * * @author Manfred Riem (mriem@manorrock.com) */ public class SoteriaPreCDIInitializerTest { /** * Test onStartup method. */ @Test public void testOnStartup() throws Exception { /* * Setup web application with BASIC auth. */ DefaultWebApplication webApplication = new DefaultWebApplication(); ServletSecurityManager securityManager = new ServletSecurityManager(); webApplication.getManager().setSecurityManager(securityManager); securityManager.setAuthMethod("BASIC"); /* * Run the onStartup of SoteriaPreCDIInitializer to configure itself * correctly for BASIC auth as per web.xml. */ SoteriaPreCDIInitializer initializer = new SoteriaPreCDIInitializer(); initializer.onStartup(null, webApplication); /* * Validate BASIC auth is configured. */ WebXmlLoginConfig webXmlLoginConfig = SoteriaServiceProviders .getServiceProvider(WebXmlLoginConfig.class); assertEquals("BASIC", webXmlLoginConfig.getAuthMethod()); } } ================================================ FILE: extension/soteria/src/test/java/cloud/piranha/extension/soteria/TestIdentityStore.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.soteria; import jakarta.enterprise.context.ApplicationScoped; import jakarta.security.enterprise.credential.UsernamePasswordCredential; import jakarta.security.enterprise.identitystore.CredentialValidationResult; import static jakarta.security.enterprise.identitystore.CredentialValidationResult.INVALID_RESULT; import jakarta.security.enterprise.identitystore.IdentityStore; import static java.util.Arrays.asList; import java.util.HashSet; /** * A test IdentityStore. * * @author Manfred Riem (mriem@manorrock.com) */ @ApplicationScoped public class TestIdentityStore implements IdentityStore { @SuppressWarnings("exports") public CredentialValidationResult validate( UsernamePasswordCredential usernamePasswordCredential) { if (usernamePasswordCredential.compareTo("test_user", "test_password")) { return new CredentialValidationResult("test_user", new HashSet<>(asList("test_group"))); } return INVALID_RESULT; } } ================================================ FILE: extension/soteria/src/test/webapp/WEB-INF/beans.xml ================================================ ================================================ FILE: extension/tempdir/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-tempdir jar Piranha - Extension - TEMPDIR cloud.piranha.core piranha-core-api ${project.version} compile cloud.piranha.core piranha-core-impl ${project.version} test org.junit.jupiter junit-jupiter-api test org.junit.platform junit-platform-launcher test default file:///tmp/piranha/extension/tempdir/ ================================================ FILE: extension/tempdir/src/main/java/cloud/piranha/extension/tempdir/TempDirExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.tempdir; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import java.lang.System.Logger; import static java.lang.System.Logger.Level.TRACE; /** * The WebApplicationExtension that configures the TEMPDIR functionality. * * @author Manfred Riem (mriem@manorrock.com) */ public class TempDirExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(TempDirExtension.class.getName()); /** * Constructor. */ public TempDirExtension() { } @Override public void configure(WebApplication webApplication) { LOGGER.log(TRACE, "Configuring the TEMPDIR extension"); if (Boolean.parseBoolean(System.getProperty("cloud.piranha.extension.tempdir.TempDirExtension.enabled", "true"))) { webApplication.addInitializer(new TempDirServletContainerInitializer()); } } } ================================================ FILE: extension/tempdir/src/main/java/cloud/piranha/extension/tempdir/TempDirServletContainerInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.tempdir; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import static jakarta.servlet.ServletContext.TEMPDIR; import jakarta.servlet.ServletException; import java.io.File; import java.lang.System.Logger; import static java.lang.System.Logger.Level.DEBUG; import static java.lang.System.Logger.Level.WARNING; import java.util.Set; /** * The ServletContainerInitializer that creates the temporary directory on the * file system and sets the context attribute to point to that directory. * * @author Manfred Riem (mriem@manorrock.com) */ public class TempDirServletContainerInitializer implements ServletContainerInitializer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(TempDirServletContainerInitializer.class.getName()); /** * Stores the constant for the temp directory location. */ private static final String TEMP_DIR_LOCATION_NAME = "cloud.piranha.extension.tempdir.location"; /** * Constructor. */ public TempDirServletContainerInitializer() { } @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { File baseDir = new File("tmp"); String tempDirLocation = servletContext.getInitParameter(TEMP_DIR_LOCATION_NAME); if (tempDirLocation != null && !tempDirLocation.isEmpty()) { baseDir = new File(tempDirLocation); if (!baseDir.isAbsolute()) { baseDir = new File("tmp", tempDirLocation); } } String name = servletContext.getContextPath(); name = name.replace("/", "_"); if (name.trim().equals("")) { name = "ROOT"; } File tempDir = new File(baseDir, name); if (!tempDir.exists()) { if (!tempDir.mkdirs()) { LOGGER.log(WARNING, "Failed to create temp directory: {0}", tempDir); } } LOGGER.log(DEBUG, "Setting TEMPDIR for context ''{0}'' to ''{1}''", servletContext.getContextPath(), tempDir); servletContext.setAttribute(TEMPDIR, tempDir); } } ================================================ FILE: extension/tempdir/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the TEMPDIR extension. * *

* The following property can be used to influence the workings of this module. *

* * * * * * * * * * *
PropertyNotes
cloud.piranha.extension.tempdir.TempDirExtension.enabledtrue to enable (default), false to disable
Configurable properties
* * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.tempdir { exports cloud.piranha.extension.tempdir; opens cloud.piranha.extension.tempdir; requires transitive cloud.piranha.core.api; } ================================================ FILE: extension/tempdir/src/site/markdown/index.md ================================================ # Piranha TEMPDIR Extension The TEMPDIR extension delivers the ability for a Piranha runtime to support the Servlet temporary directory. This extension is available by default for the following runtimes: 1. Piranha Server 1. Piranha Servlet 1. Piranha Web Profile ## Configuration parameters The following configuration parameter is available: 1. `cloud.piranha.extension.tempdir.location` - the directory where the temporary files will be stored. The default is `tmp`. ================================================ FILE: extension/tempdir/src/site/site.xml ================================================ ================================================ FILE: extension/tempdir/src/test/java/cloud/piranha/extension/tempdir/TempDirExtensionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.tempdir; import cloud.piranha.core.impl.DefaultWebApplication; import static jakarta.servlet.ServletContext.TEMPDIR; import java.io.File; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the TempDirExtension class. * * @author Manfred Riem (mriem@manorrock.com) */ class TempDirExtensionTest { /** * Test configure method. */ @Test void testConfigure() { DefaultWebApplication webApplication = new DefaultWebApplication(); TempDirExtension extension = new TempDirExtension(); extension.configure(webApplication); webApplication.initialize(); assertNotNull(webApplication.getAttribute(TEMPDIR)); File tempDir = new File("tmp/ROOT"); assertTrue(tempDir.exists()); tempDir.delete(); tempDir.getParentFile().delete(); } } ================================================ FILE: extension/tempdir/src/test/java/cloud/piranha/extension/tempdir/TempDirServletContainerInitializerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.tempdir; import cloud.piranha.core.impl.DefaultWebApplication; import java.io.File; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit test for the TempDirServletContainerInitializer class. * * @author Manfred Riem (mriem@manorrock.com) */ class TempDirServletContainerInitializerTest { /** * Test onStartup method. * * @throws Exception when a serious error occurs. */ @Test void testOnStartup() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setContextPath("my_servlet_context_name"); TempDirServletContainerInitializer initializer = new TempDirServletContainerInitializer(); initializer.onStartup(null, webApplication); File tempDir = new File("tmp/my_servlet_context_name"); assertTrue(tempDir.exists()); tempDir.delete(); tempDir.getParentFile().delete(); } /** * Test onStartup method. * * @throws Exception when a serious error occurs. */ @Test void testOnStartup2() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setContextPath(""); TempDirServletContainerInitializer initializer = new TempDirServletContainerInitializer(); initializer.onStartup(null, webApplication); File tempDir = new File("tmp/ROOT"); assertTrue(tempDir.exists()); tempDir.delete(); tempDir.getParentFile().delete(); } } ================================================ FILE: extension/tyrus/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-tyrus jar Piranha - Extension - Tyrus cloud.piranha.core piranha-core-api ${project.version} compile org.glassfish.tyrus tyrus-container-servlet compile cloud.piranha.extension piranha-extension-scinitializer ${project.version} provided cloud.piranha.core piranha-core-impl ${project.version} test org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test org.apache.maven.plugins maven-surefire-plugin false default file:///tmp/piranha/extension/tyrus ================================================ FILE: extension/tyrus/src/main/java/cloud/piranha/extension/tyrus/TyrusExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.tyrus; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import java.lang.System.Logger; import static java.lang.System.Logger.Level.DEBUG; import static java.lang.System.Logger.Level.TRACE; import org.glassfish.tyrus.servlet.TyrusServletContainerInitializer; /** * The extension that delivers Tyrus to Piranha. * * @author Manfred Riem (mriem@manorrock.com) */ public class TyrusExtension implements WebApplicationExtension { /** * Stores the property used for enabling/disabling Tyrus. */ public static final String TYRUS_ENABLED_PROPERTY = "cloud.piranha.extension.tyrus.TyrusExtension.enabled"; /** * Stores the property used for ignore initializers. */ private static final String IGNORE_INITIALIZER_PROPERTY = "cloud.piranha.extension.scinitializer.ServletContainerInitializerExtension.ignoreInitializers"; /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(TyrusExtension.class.getName()); /** * Constructor. */ public TyrusExtension() { } /** * Configure the extension. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { LOGGER.log(DEBUG, "Configuring Tyrus extension"); if (!Boolean.parseBoolean(System.getProperty(TYRUS_ENABLED_PROPERTY, "true"))) { LOGGER.log(TRACE, "Disabling Tyrus extension"); String ignoredInitializers = System.getProperty(IGNORE_INITIALIZER_PROPERTY, ""); if (ignoredInitializers.equals("")) { ignoredInitializers = TyrusServletContainerInitializer.class.getName(); } System.setProperty(IGNORE_INITIALIZER_PROPERTY, ignoredInitializers); } } } ================================================ FILE: extension/tyrus/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module integrates Tyrus into Piranha. * *

* The following properties can be used to influence the workings of this module. *

* * * * * * * * * * *
PropertyNotes
cloud.piranha.extension.tyrus.TyrusExtension.enabledtrue to enable this extension (default), false to disable
Configurable properties
* * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.tyrus { exports cloud.piranha.extension.tyrus; opens cloud.piranha.extension.tyrus; requires transitive cloud.piranha.core.api; requires org.glassfish.tyrus.container.servlet; requires static cloud.piranha.extension.scinitializer; } ================================================ FILE: extension/tyrus/src/test/java/cloud/piranha/extension/tyrus/TyrusExtensionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.tyrus; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.extension.scinitializer.ServletContainerInitializerExtension; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; /** * The JUnit tests for the TyrusExtension class. * * @author Manfred Riem (mriem@manorrock.com) */ public class TyrusExtensionTest { /** * Test configure method. */ @Test public void testConfigure() { DefaultWebApplication webApplication = new DefaultWebApplication(); TyrusExtension extension = new TyrusExtension(); extension.configure(webApplication); assertNull(System.getProperty(TyrusExtension.TYRUS_ENABLED_PROPERTY)); } /** * Test configure method and disabling Tyrus. */ @Test public void testConfigureAndDisableTyrus() { DefaultWebApplication webApplication = new DefaultWebApplication(); TyrusExtension extension = new TyrusExtension(); System.setProperty(TyrusExtension.TYRUS_ENABLED_PROPERTY, "false"); extension.configure(webApplication); assertNotNull(System.getProperty( ServletContainerInitializerExtension.IGNORE_INITIALIZERS_PROPERTY)); System.clearProperty(TyrusExtension.TYRUS_ENABLED_PROPERTY); } } ================================================ FILE: extension/wasp/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-wasp jar Piranha - Extension - WaSP The WaSP extension integrates Eclipse WaSP into Piranha. cloud.piranha.core piranha-core-impl ${project.version} compile jakarta.servlet.jsp jakarta.servlet.jsp-api compile org.glassfish.wasp wasp compile jakarta.el jakarta.el-api org.apache.ant ant eclipse jdtcore jakarta.servlet jakarta.servlet-api jakarta.el jakarta.el-api provided cloud.piranha piranha-embedded ${project.version} test org.junit.jupiter junit-jupiter-api test org.junit.platform junit-platform-launcher test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test default file:///tmp/piranha/extension/wasp org.apache.maven.plugins maven-project-info-reports-plugin 3.9.0 ================================================ FILE: extension/wasp/src/main/java/cloud/piranha/extension/wasp/WaspExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.wasp; import static java.lang.System.Logger.Level.WARNING; import java.lang.System.Logger; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import jakarta.servlet.ServletContainerInitializer; import static java.lang.System.Logger.Level.DEBUG; /** * The extension that will enable WaSP integration (aka. JSP). * * @author Manfred Riem (mriem@manorrock.com) */ public class WaspExtension implements WebApplicationExtension { /** * Stores the property to enable to extension (true) or to disable it (false). */ public static final String ENABLED_PROPERTY = "cloud.piranha.extension.wasp.WaspExtension.enabled"; /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(WaspExtension.class.getName()); /** * Constructor. */ public WaspExtension() { } /** * Configure the web application. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { if (Boolean.parseBoolean(System.getProperty(ENABLED_PROPERTY, "true"))) { LOGGER.log(DEBUG, "Configuring WaSP extension"); try { webApplication.addInitializer( webApplication.getClassLoader() .loadClass(WaspInitializer.class.getName()) .asSubclass(ServletContainerInitializer.class) .getDeclaredConstructor() .newInstance()); } catch (ReflectiveOperationException | SecurityException ex) { LOGGER.log(WARNING, "Unable to configure the WaSP extension", ex); } } else { LOGGER.log(DEBUG, "Skipping WaSP extension"); } } } ================================================ FILE: extension/wasp/src/main/java/cloud/piranha/extension/wasp/WaspInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.wasp; import cloud.piranha.core.api.WebApplication; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRegistration; import jakarta.servlet.jsp.JspFactory; import java.io.File; import static java.io.File.pathSeparator; import java.lang.System.Logger; import static java.lang.System.Logger.Level.DEBUG; import static java.lang.System.Logger.Level.TRACE; import java.util.Set; import org.glassfish.wasp.runtime.JspFactoryImpl; import org.glassfish.wasp.runtime.TldScanner; /** * The WaSP initializer. * * @author Manfred Riem (mriem@manorrock.com) */ public class WaspInitializer implements ServletContainerInitializer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(WaspInitializer.class.getName()); /** * Constructor. */ public WaspInitializer() { } /** * Initialize WaSP. * * @param classes the classes. * @param servletContext the Servlet context. * @throws ServletException when a Servlet error occurs. */ @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { WebApplication application = (WebApplication) servletContext; LOGGER.log(DEBUG, "Initializing WaSP"); if (JspFactory.getDefaultFactory() == null) { JspFactory.setDefaultFactory(new JspFactoryImpl()); } if (application.getServletRegistration("jsp") == null) { ServletRegistration.Dynamic registration = application.addServlet("jsp", "org.glassfish.wasp.servlet.JspServlet"); registration.addMapping("*.jsp"); registration.setInitParameter("classpath", getClassPath(application)); registration.setInitParameter("compilerSourceVM", "1.8"); registration.setInitParameter("compilerTargetVM", "1.8"); } application.setAttribute("org.glassfish.wasp.useMultiJarScanAlgo", true); LOGGER.log(DEBUG, "Initialized WaSP"); TldScanner scanner = new TldScanner(); scanner.onStartup(classes, servletContext); } /** * Gets the full classpath used for JSP * * @param application the application * @return the full classpath used for JSP */ private String getClassPath(WebApplication application) { String classpath = System.getProperty("jdk.module.path", System.getProperty("java.class.path")) + getClassesDirectory(application) + getJarFiles(application); LOGGER.log(TRACE, () -> "WaSP classpath is: " + classpath); return classpath; } /** * Get the WEB-INF/classes directory. * * @param servletContext the servlet context. * @return the classes directory. */ private String getClassesDirectory(ServletContext servletContext) { String classesDirectory = servletContext.getRealPath("/WEB-INF/classes"); if (classesDirectory == null) { return ""; } return pathSeparator + classesDirectory; } /** * Get the WEB-INF/lib JAR files. * * @param servletContext the servlet context. * @return the location of the JAR files. */ private String getJarFiles(ServletContext servletContext) { StringBuilder jarFiles = new StringBuilder(); String realPath = servletContext.getRealPath("/WEB-INF/lib"); if (realPath != null) { File directory = new File(realPath); if (directory.isDirectory()) { File[] files = directory.listFiles(); if (files != null) { for (File file : files) { if (file.getAbsolutePath().endsWith(".jar")) { jarFiles.append(pathSeparator); jarFiles.append(file.getAbsolutePath()); } } } } } return jarFiles.toString(); } static { // This is a hack until Jasper handles JPMS if (WaspInitializer.class.getModule().isNamed()) { String oldExtDirs = System.getProperty("java.ext.dirs", ""); System.setProperty("java.ext.dirs", System.getProperty("jdk.module.path") + pathSeparator + oldExtDirs); } } } ================================================ FILE: extension/wasp/src/main/java/cloud/piranha/extension/wasp/WaspJspManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.wasp; import jakarta.servlet.ServletRegistration; import jakarta.servlet.descriptor.JspConfigDescriptor; import cloud.piranha.core.api.JspManager; import cloud.piranha.core.api.WebApplication; /** * The WaSP manager delivered by the Jasper integration. * * @author Manfred Riem (mriem@manorrock.com) */ public class WaspJspManager implements JspManager { /** * Stores the JSP config descriptor. */ protected JspConfigDescriptor jspConfigDescriptor; /** * Constructor. */ public WaspJspManager() { } @Override public ServletRegistration.Dynamic addJspFile(WebApplication webApplication, String servletName, String jspFile) { ServletRegistration.Dynamic registration = webApplication.addServlet(servletName, new WaspServlet(jspFile)); registration.addMapping(jspFile); registration.setInitParameter("classpath", System.getProperty("java.class.path")); registration.setInitParameter("compilerSourceVM", "1.8"); registration.setInitParameter("compilerTargetVM", "1.8"); return registration; } @Override public JspConfigDescriptor getJspConfigDescriptor() { return jspConfigDescriptor; } @Override public void setJspConfigDescriptor(JspConfigDescriptor jspConfigDescriptor) { this.jspConfigDescriptor = jspConfigDescriptor; } } ================================================ FILE: extension/wasp/src/main/java/cloud/piranha/extension/wasp/WaspJspManagerExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.wasp; import static java.lang.System.Logger.Level.WARNING; import java.lang.System.Logger; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import jakarta.servlet.ServletContainerInitializer; import static java.lang.System.Logger.Level.DEBUG; /** * The extension that will enable WaSP JspManager. * * @author Manfred Riem (mriem@manorrock.com) */ public class WaspJspManagerExtension implements WebApplicationExtension { /** * Stores the property to enable to extension (true) or to disable it (false). */ public static final String ENABLED_PROPERTY = "cloud.piranha.extension.wasp.WaspJspManagerExtension.enabled"; /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(WaspJspManagerExtension.class.getName()); /** * Constructor. */ public WaspJspManagerExtension() { } /** * Configure the web application. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { if (Boolean.parseBoolean(System.getProperty(ENABLED_PROPERTY, "true"))) { LOGGER.log(DEBUG, "Configuring WaSP JspManager extension"); try { webApplication.addInitializer( webApplication.getClassLoader() .loadClass(WaspJspManagerInitializer.class.getName()) .asSubclass(ServletContainerInitializer.class) .getDeclaredConstructor() .newInstance()); } catch (ReflectiveOperationException | SecurityException ex) { LOGGER.log(WARNING, "Unable to add the WaspJspManagerInitializer", ex); } } else { LOGGER.log(DEBUG, "Skipping WaSP JspManager extension"); } } } ================================================ FILE: extension/wasp/src/main/java/cloud/piranha/extension/wasp/WaspJspManagerInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.wasp; import cloud.piranha.core.api.WebApplication; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import java.lang.System.Logger; import static java.lang.System.Logger.Level.DEBUG; import java.util.Set; /** * The WaSP JspManager initializer. * * @author Manfred Riem (mriem@manorrock.com) */ public class WaspJspManagerInitializer implements ServletContainerInitializer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(WaspJspManagerInitializer.class.getName()); /** * Constructor. */ public WaspJspManagerInitializer() { } /** * Initialize WaSP. * * @param classes the classes. * @param servletContext the Servlet context. * @throws ServletException when a Servlet error occurs. */ @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { LOGGER.log(DEBUG, "Setting the WaspJspManager"); WebApplication application = (WebApplication) servletContext; application.getManager().setJspManager(new WaspJspManager()); } } ================================================ FILE: extension/wasp/src/main/java/cloud/piranha/extension/wasp/WaspServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.wasp; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import static org.glassfish.wasp.Constants.JSP_FILE; import org.glassfish.wasp.servlet.JspServlet; /** * Servlet to set the JSP file attribute to allow WaSP find the correct file */ class WaspServlet extends JspServlet { /** * Serial version */ private static final long serialVersionUID = 1L; /** * Stores the JSP file. */ private final String jspFile; /** * Constructor. * * @param jspFile the JSP file. */ WaspServlet(String jspFile) { this.jspFile = jspFile; } @Override public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setAttribute(JSP_FILE, jspFile); super.service(request, response); } } ================================================ FILE: extension/wasp/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module integrates Eclipse WaSP into Piranha. * *

* See https://github.com/eclipse-ee4j/wasp for more information about its * project. *

*

* The following property can be used to influence the workings of this module. *

* * * * * * * * * * * * * * *
PropertyNotes
cloud.piranha.extension.wasp.WaspExtension.enabledtrue to enable (default), false to disable
cloud.piranha.extension.wasp.WaspJspManagerExtension.enabledtrue to enable (default), false to disable
Configurable properties
* * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.wasp { exports cloud.piranha.extension.wasp; opens cloud.piranha.extension.wasp; requires transitive cloud.piranha.core.impl; requires jakarta.servlet.jsp; requires transitive org.glassfish.wasp; } ================================================ FILE: extension/wasp/src/site/site.xml ================================================ ================================================ FILE: extension/wasp/src/test/java/cloud/piranha/extension/wasp/JspDescriptorTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.wasp; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.embedded.EmbeddedPiranhaBuilder; import static org.junit.jupiter.api.Assertions.assertNull; import org.junit.jupiter.api.Test; /** * JUnit tests verifying JspConfigDescriptor functionality. * * @author Manfred Riem (mriem@manorrock.com) */ class JspDescriptorTest { /** * Test getJspDescriptor method. * *

* This test validates Servlet:JAVADOC:690. *

* * @throws Exception when a serious error occurs. */ @Test void testGetJspConfigDescriptor() throws Exception { WebApplication webApplication = new DefaultWebApplication(); new EmbeddedPiranhaBuilder() .webApplication(webApplication) .initializer(WaspInitializer.class.getName()) .buildAndStart(); assertNull(webApplication.getJspConfigDescriptor()); } } ================================================ FILE: extension/wasp/src/test/java/cloud/piranha/extension/wasp/JspWriterTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.wasp; import cloud.piranha.core.api.WebApplication; import cloud.piranha.embedded.EmbeddedPiranha; import cloud.piranha.embedded.EmbeddedPiranhaBuilder; import cloud.piranha.embedded.EmbeddedRequest; import cloud.piranha.embedded.EmbeddedRequestBuilder; import cloud.piranha.embedded.EmbeddedResponse; import jakarta.servlet.ServletException; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; /** * JUnit tests verifying JspWriter functionality. * * @author Manfred Riem (mriem@manorrock.com) */ class JspWriterTest { /** * Test clearBuffer method. * * @throws Exception when a serious error occurs. */ @Test void testClearBuffer() throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .directoryResource("src/test/webapp/jspwriter") .initializer(WaspInitializer.class.getName()) .buildAndStart(); EmbeddedRequest request = new EmbeddedRequestBuilder() .servletPath("/clearBuffer.jsp") .build(); EmbeddedResponse response = new EmbeddedResponse(); piranha.service(request, response); piranha.stop().destroy(); assertEquals(200, response.getStatus()); assertFalse(response.getResponseAsString().contains("Test FAILED")); assertTrue(response.getResponseAsString().contains("Test PASSED")); } /** * Test close method. * * @throws Exception when a serious error occurs. */ @Test void testClose() throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .directoryResource("src/test/webapp/jspwriter") .initializer(WaspInitializer.class.getName()) .buildAndStart(); EmbeddedRequest request = new EmbeddedRequestBuilder() .servletPath("/close.jsp") .build(); EmbeddedResponse response = new EmbeddedResponse(); try { piranha.service(request, response); } catch (IOException ioe) { assertEquals("Stream closed", ioe.getMessage()); } piranha.stop().destroy(); } /** * Test close method. * * @throws Exception when a serious error occurs. */ @Test void testClose2() throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .directoryResource("src/test/webapp/jspwriter") .initializer(WaspInitializer.class.getName()) .buildAndStart(); EmbeddedRequest request = new EmbeddedRequestBuilder() .servletPath("/close2.jsp") .build(); EmbeddedResponse response = new EmbeddedResponse(); try { piranha.service(request, response); } catch (IOException ioe) { assertEquals("Stream closed", ioe.getMessage()); } piranha.stop().destroy(); } /** * Test close method. * * @throws Exception when a serious error occurs. */ @Test void testClose3() { try { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .directoryResource("src/test/webapp/jspwriter") .initializer(WaspInitializer.class.getName()) .buildAndStart(); EmbeddedRequest request = new EmbeddedRequestBuilder() .servletPath("/close3.jsp") .build(); EmbeddedResponse response = new EmbeddedResponse(); piranha.service(request, response); piranha.stop().destroy(); } catch (IOException | ServletException ex) { fail(); } } /** * Test close method. * * @throws Exception when a serious error occurs. */ @Test void testClose4() throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .directoryResource("src/test/webapp/jspwriter") .initializer(WaspInitializer.class.getName()) .buildAndStart(); EmbeddedRequest request = new EmbeddedRequestBuilder() .servletPath("/close4.jsp") .build(); EmbeddedResponse response = new EmbeddedResponse(); try { piranha.service(request, response); } catch (IOException ioe) { assertEquals("Stream closed", ioe.getMessage()); } WebApplication webApplication = piranha.getWebApplication(); assertNotNull(webApplication.getAttribute("flush.exception")); assertNotNull(webApplication.getAttribute("write.exception")); assertNull(webApplication.getAttribute("close.exception")); piranha.stop().destroy(); } } ================================================ FILE: extension/wasp/src/test/java/cloud/piranha/extension/wasp/WaspExtensionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */package cloud.piranha.extension.wasp; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.impl.DefaultWebApplication; import org.junit.jupiter.api.Test; /** * The JUnit tests for the WaspExtension class. * * @author Manfred Riem (mriem@manorrock.com) */ public class WaspExtensionTest { /** * Test configure method. */ @Test public void testConfigure() { WebApplication webApplication = new DefaultWebApplication(); WaspExtension extension = new WaspExtension(); extension.configure(webApplication); } /** * Test configure method. */ @Test public void testConfigureWhenEnabledPropertyIsFalse() { System.setProperty(WaspExtension.ENABLED_PROPERTY, "false"); WebApplication webApplication = new DefaultWebApplication(); WaspExtension extension = new WaspExtension(); extension.configure(webApplication); System.clearProperty(WaspExtension.ENABLED_PROPERTY); } } ================================================ FILE: extension/wasp/src/test/java/cloud/piranha/extension/wasp/WaspInitializerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.wasp; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.resource.impl.DirectoryResource; import java.io.File; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the WaspInitializer. * * @author Manfred Riem (mriem@manorrock.com) */ class WaspInitializerTest { /** * Test onStartup method. * * @throws Exception when a serious error occurs. */ @Test void testOnStartup() throws Exception { DefaultWebApplication webApp = new DefaultWebApplication(); webApp.addResource(new DirectoryResource(new File("src/test/webapp/waspinitializer"))); webApp.addInitializer(new WaspInitializer()); webApp.initialize(); assertTrue(webApp.isInitialized()); } } ================================================ FILE: extension/wasp/src/test/java/cloud/piranha/extension/wasp/WaspJspManagerExtensionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */package cloud.piranha.extension.wasp; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.impl.DefaultWebApplication; import org.junit.jupiter.api.Test; /** * The JUnit tests for the WaspExtension class. * * @author Manfred Riem (mriem@manorrock.com) */ public class WaspJspManagerExtensionTest { /** * Test configure method. */ @Test public void testConfigure() { WebApplication webApplication = new DefaultWebApplication(); WaspJspManagerExtension extension = new WaspJspManagerExtension(); extension.configure(webApplication); } /** * Test configure method. */ @Test public void testConfigureWhenEnabledPropertyIsFalse() { System.setProperty(WaspJspManagerExtension.ENABLED_PROPERTY, "false"); WebApplication webApplication = new DefaultWebApplication(); WaspJspManagerExtension extension = new WaspJspManagerExtension(); extension.configure(webApplication); System.clearProperty(WaspJspManagerExtension.ENABLED_PROPERTY); } } ================================================ FILE: extension/wasp/src/test/webapp/jspwriter/clearBuffer.jsp ================================================ <%@page contentType="text/plain" pageEncoding="UTF-8" import="java.io.*" autoFlush="false"%> <% if (out.getBufferSize() > 0) { out.println("Arbitrary text"); out.flush(); out.println("Test FAILED"); try { out.clearBuffer(); } catch (Throwable t) { if (t instanceof IOException) { out.println("Test FAILED"); return; } else { out.println("Test PASSED"); } } out.println("Test PASSED"); } else { out.println("Test PASSED"); } %> ================================================ FILE: extension/wasp/src/test/webapp/jspwriter/close.jsp ================================================ <%@page contentType="text/plain" pageEncoding="UTF-8" import="java.io.*" autoFlush="false"%> <% out.close(); out.flush();%> ================================================ FILE: extension/wasp/src/test/webapp/jspwriter/close2.jsp ================================================ <%@page contentType="text/plain" pageEncoding="UTF-8" import="java.io.*" autoFlush="false"%> <% out.close(); out.println();%> ================================================ FILE: extension/wasp/src/test/webapp/jspwriter/close3.jsp ================================================ <%@page contentType="text/plain" pageEncoding="UTF-8" import="java.io.*" autoFlush="false"%> <% out.close(); out.close();%> ================================================ FILE: extension/wasp/src/test/webapp/jspwriter/close4.jsp ================================================ <%@page contentType="text/plain" pageEncoding="UTF-8" import="java.io.*" autoFlush="false"%> <% out.close(); ServletContext ctx = (ServletContext) application; try { out.flush(); } catch (Throwable t) { if (t instanceof IOException) { ctx.setAttribute("flush.exception", t); } } try { out.println(); } catch (Throwable t) { if (t instanceof IOException) { ctx.setAttribute("write.exception", t); } } try { out.close(); } catch (Throwable t) { ctx.setAttribute("close.exception", t); } %> ================================================ FILE: extension/wasp/src/test/webapp/waspinitializer/WEB-INF/classes/KEEPME ================================================ ================================================ FILE: extension/wasp/src/test/webapp/waspinitializer/WEB-INF/lib/KEEPME ================================================ ================================================ FILE: extension/webprofile/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-webprofile jar Piranha - Extension - Web Profile This module delivers the default set of extensions for Piranha Micro and Piranha Server, namely Jakarta Web Profile. cloud.piranha.core piranha-core-api ${project.version} compile cloud.piranha.extension piranha-extension-annotationscan ${project.version} compile cloud.piranha.extension piranha-extension-annotationscan-classfile ${project.version} compile cloud.piranha.extension piranha-extension-declared ${project.version} compile cloud.piranha.extension piranha-extension-fileupload ${project.version} compile cloud.piranha.extension piranha-extension-datasource ${project.version} compile cloud.piranha.extension piranha-extension-concurro ${project.version} compile cloud.piranha.extension piranha-extension-expressly ${project.version} compile cloud.piranha.extension piranha-extension-hibernate-validator ${project.version} compile cloud.piranha.extension piranha-extension-jersey ${project.version} compile cloud.piranha.extension piranha-extension-jstl ${project.version} compile cloud.piranha.extension piranha-extension-mojarra ${project.version} compile cloud.piranha.extension piranha-extension-tyrus ${project.version} compile cloud.piranha.extension piranha-extension-wasp ${project.version} compile cloud.piranha.extension piranha-extension-eclipselink ${project.version} compile cloud.piranha.extension piranha-extension-herring ${project.version} compile cloud.piranha.extension piranha-extension-omnifish-transact ${project.version} compile cloud.piranha.extension piranha-extension-policy ${project.version} compile cloud.piranha.extension piranha-extension-scinitializer ${project.version} compile cloud.piranha.extension piranha-extension-security-servlet ${project.version} compile cloud.piranha.extension piranha-extension-servletannotations ${project.version} compile cloud.piranha.extension piranha-extension-tempdir ${project.version} compile cloud.piranha.extension piranha-extension-webxml ${project.version} compile cloud.piranha.extension piranha-extension-welcomefile ${project.version} compile cloud.piranha.extension piranha-extension-weld ${project.version} compile jakarta.annotation jakarta.annotation-api runtime org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test ================================================ FILE: extension/webprofile/src/main/java/cloud/piranha/extension/webprofile/WebProfileExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webprofile; import cloud.piranha.core.api.WebApplicationExtension; import cloud.piranha.core.api.WebApplicationExtensionContext; import cloud.piranha.extension.annotationscan.AnnotationScanExtension; import cloud.piranha.extension.annotationscan.classfile.ClassfileAnnotationScanExtension; import cloud.piranha.extension.datasource.DefaultDatasourceExtension; import cloud.piranha.extension.declared.DeclaredExtension; import cloud.piranha.extension.eclipselink.EclipseLinkExtension; import cloud.piranha.extension.fileupload.FileUploadExtension; import cloud.piranha.extension.herring.HerringExtension; import cloud.piranha.extension.policy.PolicyExtension; import cloud.piranha.extension.scinitializer.ServletContainerInitializerExtension; import cloud.piranha.extension.security.servlet.ServletSecurityExtension; import cloud.piranha.extension.security.servlet.ServletSecurityManagerExtension; import cloud.piranha.extension.servletannotations.ServletAnnotationsExtension; import cloud.piranha.extension.tempdir.TempDirExtension; import cloud.piranha.extension.transact.TransactExtension; import cloud.piranha.extension.wasp.WaspExtension; import cloud.piranha.extension.wasp.WaspJspManagerExtension; import cloud.piranha.extension.webxml.WebXmlExtension; import cloud.piranha.extension.welcomefile.WelcomeFileExtension; import cloud.piranha.extension.weld.WeldExtension; /** * The extension that delivers Web Profile support. * * @author Manfred Riem (mriem@manorrock.com) */ public class WebProfileExtension implements WebApplicationExtension { /** * Constructor. */ public WebProfileExtension() { } @Override public void extend(WebApplicationExtensionContext context) { context.add(PolicyExtension.class); // JavaPolicy context.add(TempDirExtension.class); // TEMPDIR context.add(WelcomeFileExtension.class); // welcome-file context.add(ServletSecurityManagerExtension.class); // SecurityManager context.add(FileUploadExtension.class); // Servlet Part API support context.add(WaspJspManagerExtension.class); // addJspFile context.add(HerringExtension.class); // Herring (JNDI) context.add(WebXmlExtension.class); // web.xml context.add(getAnnotationScanExtensionClass()); // Annotation scanning context.add(ServletAnnotationsExtension.class); // Servlet annotations context.add(DeclaredExtension.class); // context.add(WeldExtension.class); // CDI / Weld context.add(DefaultDatasourceExtension.class); // Default data source context.add(TransactExtension.class); // OmniFish Transaction context.add(ServletSecurityExtension.class); // Security implementation context.add(WaspExtension.class); // WaSP context.add(EclipseLinkExtension.class); // Jakarta Persistence context.add(ServletContainerInitializerExtension.class); // ServletContainerInitializer } private static Class getAnnotationScanExtensionClass() { if (System.getProperty(ClassfileAnnotationScanExtension.EXPERIMENTAL_PROPERTY) != null) { return ClassfileAnnotationScanExtension.class; // Annotation scanning using the new Classfile API } return AnnotationScanExtension.class; // Annotation scanning } } ================================================ FILE: extension/webprofile/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the meta extension for Jakarta Web Profile. * *

* The following extensions and/or dependencies are delivered as part of this * meta extension: *

*
    *
  • Annotation Scanning
  • *
  • File Upload
  • *
  • Default DataSource
  • *
  • Eclipse Expressly (EL)
  • *
  • Eclipse Jersey (REST)
  • *
  • Herring (JNDI)
  • *
  • Java Policy
  • *
  • ServletContainerInitializer
  • *
  • Servlet Security
  • *
  • Servlet Annotations
  • *
  • TEMPDIR
  • *
  • OmniFish Transact
  • *
  • WaSP (Pages)
  • *
  • web.xml
  • *
  • Welcome File
  • *
*/ module cloud.piranha.extension.webprofile { exports cloud.piranha.extension.webprofile; opens cloud.piranha.extension.webprofile; requires transitive cloud.piranha.core.api; requires cloud.piranha.extension.annotationscan; requires cloud.piranha.extension.annotationscan.classfile; requires cloud.piranha.extension.declared; requires cloud.piranha.extension.fileupload; requires cloud.piranha.extension.expressly; requires cloud.piranha.extension.datasource; requires cloud.piranha.extension.eclipselink; requires cloud.piranha.extension.herring; requires cloud.piranha.extension.jersey; requires cloud.piranha.extension.policy; requires cloud.piranha.extension.scinitializer; requires cloud.piranha.extension.security.servlet; requires cloud.piranha.extension.servletannotations; requires cloud.piranha.extension.tempdir; requires cloud.piranha.extension.transact; requires cloud.piranha.extension.wasp; requires cloud.piranha.extension.webxml; requires cloud.piranha.extension.welcomefile; requires cloud.piranha.extension.weld; } ================================================ FILE: extension/webxml/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-webxml jar Piranha - Extension - web.xml cloud.piranha.core piranha-core-api ${project.version} compile cloud.piranha.core piranha-core-impl ${project.version} compile cloud.piranha.resource piranha-resource-impl ${project.version} compile cloud.piranha.extension piranha-extension-herring ${project.version} test cloud.piranha.extension piranha-extension-welcomefile ${project.version} test cloud.piranha.extension piranha-extension-wasp ${project.version} test com.h2database h2 test org.junit.jupiter junit-jupiter-engine test org.junit.platform junit-platform-launcher test org.apache.maven.plugins maven-surefire-plugin false org.jacoco jacoco-maven-plugin check check BUNDLE INSTRUCTION COVEREDRATIO 0.78 BRANCH COVEREDRATIO 0.64 ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/WebXmlExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml; import java.lang.reflect.InvocationTargetException; import java.lang.System.Logger.Level; import java.lang.System.Logger; import jakarta.servlet.ServletContainerInitializer; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; /** * The extension for web.xml processing. * * @author Manfred Riem (mriem@manorrock.com) */ public class WebXmlExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(WebXmlExtension.class.getName()); /** * Constructor. */ public WebXmlExtension() { } /** * Configure the web application. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { try { ClassLoader classLoader = webApplication.getClassLoader(); Class clazz = classLoader. loadClass(WebXmlInitializer.class.getName()) .asSubclass(ServletContainerInitializer.class); ServletContainerInitializer initializer = clazz.getDeclaredConstructor().newInstance(); webApplication.addInitializer(initializer); } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { LOGGER.log(Level.WARNING, "Unable to enable the WebXmlExtension", ex); } } } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/WebXmlInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml; import cloud.piranha.extension.webxml.internal.WebXmlParser; import cloud.piranha.extension.webxml.internal.WebXmlProcessor; import cloud.piranha.extension.webxml.internal.WebXmlManager; import cloud.piranha.core.api.WebApplication; import cloud.piranha.extension.webxml.internal.WebXml; import cloud.piranha.extension.webxml.internal.WebXmlServletMapping; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import java.io.IOException; import java.io.InputStream; import java.lang.System.Logger; import static java.lang.System.Logger.Level.DEBUG; import static java.lang.System.Logger.Level.WARNING; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; /** * The web.xml initializer. * * @author Manfred Riem (mriem@manorrock.com) */ public class WebXmlInitializer implements ServletContainerInitializer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(WebXmlInitializer.class.getName()); /** * Constructor. */ public WebXmlInitializer() { } /** * On startup. * * @param classes the classes. * @param servletContext the servlet context. * @throws ServletException when a servlet error occurs. */ @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { LOGGER.log(DEBUG, () -> "Entering WebXmlInitializer.onStartup"); try { WebApplication webApplication = (WebApplication) servletContext; WebXmlManager manager = new WebXmlManager(); webApplication.setAttribute("cloud.piranha.extension.webxml.WebXmlManager", manager); WebXmlParser parser = new WebXmlParser(); InputStream inputStream = servletContext.getResourceAsStream("WEB-INF/web.xml"); if (inputStream != null) { WebXml webXml = parser.parse(servletContext.getResourceAsStream("WEB-INF/web.xml")); manager.setWebXml(webXml); manager.setInitialWebXml(webXml); } ArrayList webFragments = new ArrayList<>(); List webFragmentUrls = Collections.list(servletContext.getClassLoader().getResources("META-INF/web-fragment.xml")); for (URL url : webFragmentUrls) { try (InputStream stream = url.openStream()) { WebXml webFragment = parser.parse(stream); webFragment.setFragment(true); webFragments.add(webFragment); } } if (!webFragments.isEmpty()) { manager.setWebFragments(webFragments); } if (manager.getWebXml() == null) { manager.setWebXml(new WebXml()); } if (manager.getWebXml() != null) { WebXml webXml = manager.getWebXml(); WebXmlProcessor processor = new WebXmlProcessor(); processor.process(webXml, webApplication); if (webXml.getMetadataComplete()) { return; } removeExistingServletMappings(webApplication, manager); manager.getOrderedFragments().forEach(fragment -> processor.process(fragment, webApplication)); } else { LOGGER.log(DEBUG, "No web.xml found!"); } } catch (IOException e) { LOGGER.log(WARNING, "Unable to parse web.xml", e); } LOGGER.log(DEBUG, () -> "Exiting WebXmlInitializer.onStartup"); } /** * Remove any servlet mappings in the web fragments that are already mapped * by the regular web.xml. * * @param webApp the web application. * @param manager the web.xml manager. */ private void removeExistingServletMappings(WebApplication webApp, WebXmlManager manager) { for(WebXmlServletMapping mapping : manager.getWebXml().getServletMappings()) { for(WebXml fragment : manager.getOrderedFragments()) { ArrayList candidateList = new ArrayList<>(fragment.getServletMappings()); for(WebXmlServletMapping candidateMapping : candidateList) { if (candidateMapping.servletName().equals(mapping.servletName())) { fragment.getServletMappings().remove(candidateMapping); } } } } } } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXml.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; /** * The web.xml in object format. * * @author Manfred Riem (mriem@manorrock.com) */ public class WebXml implements Serializable { /** * Stores the others tag. */ public static final String OTHERS_TAG = WebXml.class.getName() + ".ordering.others"; /** * Stores the serial version UID. */ private static final long serialVersionUID = 6143204024206508136L; /** * Stores the major version. */ private int majorVersion = -1; /** * Stores the minor version. */ private int minorVersion = -1; /** * Stores the security constraints */ private transient List securityConstraints = new ArrayList<>(); /** * Stores the metadata complete flag. */ private boolean metadataComplete; /** * Stores the absolute ordering. */ private List absoluteOrdering; /** * Stores the locale encoding mapping. */ private Map localeEncodingMapping = new HashMap<>(); /** * A class used to deal with before/after ordering. */ public static class RelativeOrder { /** * Stores the before. */ private List before; /** * Stores the after. */ private List after; /** * Constructor. */ public RelativeOrder() { before = Collections.emptyList(); after = Collections.emptyList(); } /** * Constructor. * * @param before the before. * @param after the after. */ public RelativeOrder(List before, List after) { this.before = Objects.requireNonNullElseGet(before, Collections::emptyList); this.after = Objects.requireNonNullElseGet(after, Collections::emptyList); } /** * {@return the before} */ public List getBefore() { return before; } /** * Set the before. * * @param before the before. */ public void setBefore(List before) { this.before = before; } /** * {@return the after} */ public List getAfter() { return after; } /** * Set the after. * * @param after the after. */ public void setAfter(List after) { this.after = after; } } /** * Stores the relative ordering. */ private transient RelativeOrder relativeOrdering; // ------------------------------------------------------------------------- /** * Stores the context params. */ private final List contextParams = new ArrayList<>(); /** * Stores the default context path. */ private String defaultContextPath; /** * Stores the data sources. */ private final List dataSources = new ArrayList<>(); /** * Store if we are denying uncovered HTTP methods. */ private boolean denyUncoveredHttpMethods; /** * Store the display name. */ private String displayName; /** * Store if we are distributable. */ private boolean distributable; /** * Stores the error pages. */ private final List errorPages = new ArrayList<>(); /** * Stores the filters. */ private final List filters = new ArrayList<>(); /** * Stores the filter mappings. */ private final List filterMappings = new ArrayList<>(); /** * Stores if we are a fragment. */ private boolean fragment; /** * Stores the fragment name. */ private String fragmentName; /** * Stores the JSP config. */ private WebXmlJspConfig jspConfig = new WebXmlJspConfig(); /** * Stores the listeners. */ private final List listeners = new ArrayList<>(); /** * Stores the login config. */ private transient WebXmlLoginConfig loginConfig; /** * Stores the mime mappings. */ private final List mimeMappings = new ArrayList<>(); /** * Stores the request character encoding. */ private String requestCharacterEncoding; /** * Stores the response character encoding. */ private String responseCharacterEncoding; /** * Set of all unique role names that have either been explicitly declared, * or used in a constraint. */ private final Set roleNames = new HashSet<>(); /** * Stores the servlets. */ private final List servlets = new ArrayList<>(); /** * Stores the servlet mappings. */ private final List servletMappings = new ArrayList<>(); /** * Stores the session configuration. */ private transient WebXmlSessionConfig sessionConfig; /** * Stores the welcome files. */ private final List welcomeFiles = new ArrayList<>(); /** * Default constructor. */ public WebXml() { } /** * {@return the context params} */ public List getContextParams() { return contextParams; } /** * {@return the default context path} */ public String getDefaultContextPath() { return defaultContextPath; } /** * Get the data sources. * * @return the data sources. */ public List getDataSources() { return dataSources; } /** * Get if we are denying uncovered HTTP methods. * * @return true if we are, false otherwise. */ public boolean getDenyUncoveredHttpMethods() { return denyUncoveredHttpMethods; } /** * {@return the display name} */ public String getDisplayName() { return displayName; } /** * {@return the error pages} */ public List getErrorPages() { return errorPages; } /** * {@return the filters} */ public List getFilters() { return filters; } /** * {@return the filter mappings} */ public List getFilterMappings() { return filterMappings; } /** * {@return the fragment name} */ public String getFragmentName() { return fragmentName; } /** * {@return the jsp config} */ public WebXmlJspConfig getJspConfig() { return jspConfig; } /** * {@return the login config} */ public WebXmlLoginConfig getLoginConfig() { return loginConfig; } /** * {@return the listeners} */ public List getListeners() { return listeners; } /** * {@return the mime mappings} */ public List getMimeMappings() { return mimeMappings; } /** * {@return the request character encoding} */ public String getRequestCharacterEncoding() { return requestCharacterEncoding; } /** * {@return the response character encoding} */ public String getResponseCharacterEncoding() { return responseCharacterEncoding; } /** * Get all the unique role names that have either been explicitly declared, * or used in a constraint. * * @return the unique role names that have either been explicitly declared, * or used in a constraint. */ public Set getRoleNames() { return roleNames; } /** * {@return the servlets} */ public List getServlets() { return servlets; } /** * {@return the servlet mappings} */ public List getServletMappings() { return servletMappings; } /** * {@return the session config} */ public WebXmlSessionConfig getSessionConfig() { return sessionConfig; } /** * Get the welcome files. * * @return welcome files. */ public List getWelcomeFiles() { return welcomeFiles; } /** * Is the application distributable. * * @return true if it is, false otherwise. */ public boolean isDistributable() { return distributable; } /** * Is this a web-fragment. * * @return true if it, false otherwise. */ public boolean isFragment() { return fragment; } /** * Set the default context path. * * @param defaultContextPath the default context path. */ public void setDefaultContextPath(String defaultContextPath) { this.defaultContextPath = defaultContextPath; } /** * Set if we are denying uncovered HTTP methods. * * @param denyUncoveredHttpMethods the boolean value. */ public void setDenyUncoveredHttpMethods(boolean denyUncoveredHttpMethods) { this.denyUncoveredHttpMethods = denyUncoveredHttpMethods; } /** * Set the display name. * * @param displayName the display name. */ public void setDisplayName(String displayName) { this.displayName = displayName; } /** * Set if we are distributable. * * @param distributable the boolean value. */ public void setDistributable(boolean distributable) { this.distributable = distributable; } /** * Set if we are a fragment. * * @param fragment the boolean value. */ public void setFragment(boolean fragment) { this.fragment = fragment; } /** * Set the fragment name. * * @param fragmentName the fragment name. */ public void setFragmentName(String fragmentName) { this.fragmentName = fragmentName; } /** * Set the JSP config. * * @param jspConfig the jsp config. */ public void setJspConfig(WebXmlJspConfig jspConfig) { this.jspConfig = jspConfig; } /** * Set the login config. * * @param loginConfig the login config. */ public void setLoginConfig(WebXmlLoginConfig loginConfig) { this.loginConfig = loginConfig; } /** * Set the request character encoding. * * @param requestCharacterEncoding the request character encoding. */ public void setRequestCharacterEncoding(String requestCharacterEncoding) { this.requestCharacterEncoding = requestCharacterEncoding; } /** * Set the response character encoding. * * @param responseCharacterEncoding the response character encoding. */ public void setResponseCharacterEncoding(String responseCharacterEncoding) { this.responseCharacterEncoding = responseCharacterEncoding; } /** * Set the session config. * * @param sessionConfig the session comfig. */ public void setSessionConfig(WebXmlSessionConfig sessionConfig) { this.sessionConfig = sessionConfig; } /** * Gets the major version as set by the version attribute in the web app * element * * @return the major version. */ public int getMajorVersion() { return majorVersion; } /** * Sets the major version as set by the version attribute in the web app * element * * @param majorVersion the major version. */ public void setMajorVersion(int majorVersion) { this.majorVersion = majorVersion; } /** * Gets the minor version as set by the version attribute in the web app * element * * @return the major version. */ public int getMinorVersion() { return minorVersion; } /** * Sets the minor version as set by the version attribute in the web app * element * * @param minorVersion the minor version. */ public void setMinorVersion(int minorVersion) { this.minorVersion = minorVersion; } /** * Set the metadata complete flag. * * @param metadataComplete the metadata complete */ public void setMetadataComplete(boolean metadataComplete) { this.metadataComplete = metadataComplete; } /** * Get the metadata complete flag. * * @return the metadata complete */ public boolean getMetadataComplete() { return metadataComplete; } /** * {@return the absolute ordering} */ public List getAbsoluteOrdering() { return absoluteOrdering; } /** * {@return the relative ordering} */ public RelativeOrder getRelativeOrdering() { return relativeOrdering; } /** * Set the absolute ordering. * * @param absoluteOrdering the absolute ordering. */ public void setAbsoluteOrdering(List absoluteOrdering) { this.absoluteOrdering = absoluteOrdering; } /** * Set the relative ordering. * * @param relativeOrdering the relative ordering. */ public void setRelativeOrdering(RelativeOrder relativeOrdering) { this.relativeOrdering = relativeOrdering; } /** * {@return the locale encoding mapping} */ public Map getLocaleEncodingMapping() { return this.localeEncodingMapping; } /** * Set the locale encoding mapping. * * @param localeEncodingMapping the locale encoding mapping. */ public void setLocaleEncodingMapping(Map localeEncodingMapping) { this.localeEncodingMapping = localeEncodingMapping; } /** * Get the security constraints. * * @return the security constraints. */ public List getSecurityConstraints() { return securityConstraints; } /** * Set the security constraints. * * @param securityConstraints the security constraints. */ public void setSecurityConstraints(List securityConstraints) { this.securityConstraints = securityConstraints; } } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlContextParam.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; /** * A context-param inside of web.xml/web-fragment.xml. * * @param name the name. * @param value the value. * @author Manfred Riem (mriem@manorrock.com) */ public record WebXmlContextParam(String name, String value) { } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlDataSource.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import java.util.HashMap; import java.util.Map; /** * A data-source inside of web.xml. * * @author Manfred Riem (mriem@manorrock.com) */ public class WebXmlDataSource { /** * Stores the class name. */ private String className; /** * Stores the name. */ private String name; /** * Stores the password. */ private String password; /** * Stores the URL. */ private String url; /** * Stores the user. */ private String user; /** * Stores the properties. */ private Map properties = new HashMap<>(); /** * Default constructor. */ public WebXmlDataSource() { } /** * Get the class name. * * @return the class name. */ public String getClassName() { return className; } /** * Get the name. * * @return the name. */ public String getName() { return name; } /** * Get the password. * * @return the password. */ public String getPassword() { return password; } /** * Get the URL. * * @return the URL. */ public String getUrl() { return url; } /** * Get the user. * * @return the user. */ public String getUser() { return user; } /** * Set the user. * * @param user the user. */ public void setUser(String user) { this.user = user; } /** * Set the class name. * * @param className the class name. */ public void setClassName(String className) { this.className = className; } /** * Set the name. * * @param name the name. */ public void setName(String name) { this.name = name; } /** * Set the password. * * @param password the password. */ public void setPassword(String password) { this.password = password; } /** * Set the URL. * * @param url the URL. */ public void setUrl(String url) { this.url = url; } /** * Get the properties * * @return the properties */ public Map getProperties() { return properties; } /** * Set the properties * * @param properties the properties */ public void setProperties(Map properties) { this.properties = properties; } } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlErrorPage.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; /** * An error-page inside of web.xml/web-fragment.xml. * * @param errorCode the error code. * @param exceptionType the exception type. * @param location the location. * @author Manfred Riem (mriem@manorrock.com) */ public record WebXmlErrorPage(String errorCode, String exceptionType, String location) { } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlFilter.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import java.util.ArrayList; import java.util.List; /** * A filter inside of web.xml/web-fragment.xml. * * @author Manfred Riem (mriem@manorrock.com) */ public class WebXmlFilter { /** * Stores if async is supported. */ private boolean asyncSupported; /** * Stores the class name. */ private String className; /** * Stores the filter name. */ private String filterName; /** * Stores the init parameters. */ private final List initParams = new ArrayList<>(); /** * Stores the servlet name. */ private String servletName; /** * Constructor. */ public WebXmlFilter() { } /** * Add init param. * * @param initParam the init param. */ public void addInitParam(WebXmlFilterInitParam initParam) { this.initParams.add(initParam); } /** * Get the class name. * * @return the class name. */ public String getClassName() { return className; } /** * Get the filter name. * * @return the filter name. */ public String getFilterName() { return filterName; } /** * Get the init parameters. * * @return the init parameters. */ public List getInitParams() { return initParams; } /** * Get the servlet name. * * @return the servlet name. */ public String getServletName() { return servletName; } /** * Is async supported. * * @return true if it is, false otherwise. */ public boolean isAsyncSupported() { return asyncSupported; } /** * Set if async is supported. * * @param asyncSupported the boolean value. */ public void setAsyncSupported(boolean asyncSupported) { this.asyncSupported = asyncSupported; } /** * Set the class name. * * @param className the class name. */ public void setClassName(String className) { this.className = className; } /** * Set the filter name. * * @param filterName the filter name. */ public void setFilterName(String filterName) { this.filterName = filterName; } /** * Set the servlet name. * * @param servletName the servlet name. */ public void setServletName(String servletName) { this.servletName = servletName; } } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlFilterInitParam.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; /** * A filter init-param inside of web.xml/web-fragment.xml. * * @param name the name. * @param value the value. * @author Manfred Riem (mriem@manorrock.com) */ public record WebXmlFilterInitParam(String name, String value) { } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlFilterMapping.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import java.util.ArrayList; import java.util.List; /** * A filter-mapping inside of web.xml/web-fragment.xml. * * @author Manfred Riem (mriem@manorrock.com) */ public class WebXmlFilterMapping { /** * The type of dispatch on which the filter is invoked. * *

* Valid values are FORWARD, INCLUDE, REQUEST, and ERROR. *

*/ private List dispatchers = new ArrayList<>(); /** * Stores the filter name. */ private String filterName; /** * Stores the servlet names to which a filter is mapped. */ private List servletNames = new ArrayList<>(); /** * Stores the URL pattern. */ private List urlPatterns = new ArrayList<>(); /** * Default constructor. */ public WebXmlFilterMapping() { } /** * Get the dispatchers. * * @return the dispatchers. */ public List getDispatchers() { return dispatchers; } /** * Get the filter name. * * @return the filter name. */ public String getFilterName() { return filterName; } /** * Get the servlet names. * * @return the servlet names. */ public List getServletNames() { return servletNames; } /** * Get the URL patterns. * * @return the URL patterns. */ public List getUrlPatterns() { return urlPatterns; } /** * Set the dispatchers. * * @param dispatchers the dispatchers. */ public void setDispatchers(List dispatchers) { this.dispatchers = dispatchers; } /** * Set the filter name. * * @param filterName the filter name. */ public void setFilterName(String filterName) { this.filterName = filterName; } /** * Set the servlet names. * * @param servletNames the servlet names. */ public void setServletNames(List servletNames) { this.servletNames = servletNames; } /** * Set the URL patterns. * * @param urlPatterns the URL patterns. */ public void setUrlPatterns(List urlPatterns) { this.urlPatterns = urlPatterns; } } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlJspConfig.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import java.util.ArrayList; import java.util.List; /** * A jsp-config inside web.xml/web-fragment.xml. * * @author Manfred Riem (mriem@manorrock.com) */ public class WebXmlJspConfig { /** * Stores the list of taglib. */ private List taglibs = new ArrayList<>(); /** * Default constructor. */ public WebXmlJspConfig() { } /** * Get the list of taglib. * * @return the list. */ public List getTaglibs() { return taglibs; } } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlJspConfigTaglib.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; /** * A taglib inside jsp-config of web.xml/web-fragment.xml. * * @author Manfred Riem (mriem@manorrock.com) */ public class WebXmlJspConfigTaglib { /** * Stores the location. */ private final String location; /** * Stores the URI. */ private final String uri; /** * Constructor. * * @param location the location. * @param uri the URI. */ public WebXmlJspConfigTaglib(String location, String uri) { this.location = location; this.uri = uri; } /** * Get the location. * * @return the location. */ public String getLocation() { return location; } /** * Get the URI. * * @return the URI. */ public String getUri() { return uri; } } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlListener.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; /** * A listener inside of web.xml/web-fragment.xml. * * @param className the class name. * @author Manfred Riem (mriem@manorrock.com) */ public record WebXmlListener(String className) { } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlLoginConfig.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; /** * A login-config inside of web.xml/web-fragment.xml. * * @param authMethod the auth method. * @param realmName the realm name. * @param formLoginPage the form login page. * @param formErrorPage the form error page. * @author Manfred Riem (mriem@manorrock.com) */ public record WebXmlLoginConfig( String authMethod, String realmName, String formLoginPage, String formErrorPage) { } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import static cloud.piranha.extension.webxml.internal.WebXml.OTHERS_TAG; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Consumer; /** * The web.xml manager. * * @author Manfred Riem (mriem@manorrock.com) */ public class WebXmlManager { /** * Stores the initial web.xml */ private WebXml initialWebXml; /** * Stores the web fragments. */ private List webFragments = new ArrayList<>(); /** * Stores the web.xml. */ private WebXml webXml; /** * {@return the initial web.xml} */ public WebXml getInitialWebXml() { return initialWebXml; } /** * Set the initial web.xml. * * @param initialWebXml the initial web.xml. */ public void setInitialWebXml(WebXml initialWebXml) { this.initialWebXml = initialWebXml; } /** * {@return the web fragments} */ public List getWebFragments() { return webFragments; } /** * Set the web fragments. * * @param webFragments the web fragments. */ public void setWebFragments(List webFragments) { this.webFragments = webFragments; } /** * Get the web.xml. * * @return the web.xml. */ public WebXml getWebXml() { return webXml; } /** * Set the web.xml. * * @param webXml the web.xml. */ public void setWebXml(WebXml webXml) { this.webXml = webXml; } /** * {@return the ordered fragments} */ public List getOrderedFragments() { if (webXml == null) return Collections.emptyList(); if (webXml.getAbsoluteOrdering() != null) { return processAbsoluteOrdering(); } return processRelativeOrdering(); } private List processAbsoluteOrdering() { List absoluteOrdering = webXml.getAbsoluteOrdering().stream().distinct().toList(); if (absoluteOrdering.isEmpty()) return Collections.emptyList(); if (!absoluteOrdering.contains(OTHERS_TAG)) { return getWebFragments() .stream() .filter(x -> absoluteOrdering.contains(x.getFragmentName())) .sorted(Comparator.comparingInt(x -> absoluteOrdering.indexOf(x.getFragmentName()))) .toList(); } int indexOfOthersTag = absoluteOrdering.indexOf(OTHERS_TAG); List fragmentsBeforeOthers = absoluteOrdering.stream() .filter(x -> absoluteOrdering.indexOf(x) < indexOfOthersTag) .toList(); List fragmentsAfterOthers = absoluteOrdering.stream() .filter(x -> absoluteOrdering.indexOf(x) > indexOfOthersTag) .toList(); List othersFragments = getWebFragments() .stream() .map(WebXml::getFragmentName) .filter(x -> !fragmentsAfterOthers.contains(x) && !fragmentsBeforeOthers.contains(x)) .toList(); List orderedFragments = new ArrayList<>(); for (String fragmentName : absoluteOrdering) { if (OTHERS_TAG.equals(fragmentName)) { orderedFragments.addAll(toWebXml(othersFragments)); continue; } findWebFragment(fragmentName).ifPresent(orderedFragments::add); } return orderedFragments; } private List toWebXml(List fragmentNames) { return getWebFragments().stream().filter(x -> fragmentNames.contains(x.getFragmentName())).toList(); } private Optional findWebFragment(String fragmentName) { return getWebFragments().stream().filter(x -> fragmentName.equals(x.getFragmentName())).findFirst(); } private List processRelativeOrdering() { if (webFragments.stream().noneMatch(x -> x.getRelativeOrdering() != null)) { return webFragments; } List sorted = new ArrayList<>(webFragments); Set others = new LinkedHashSet<>(); Set beforeOthers = new LinkedHashSet<>(); Set afterOthers = new LinkedHashSet<>(); // Separate the fragments that are before/after others and the others webFragments.forEach(webFragment -> { WebXml.RelativeOrder relativeOrdering = webFragment.getRelativeOrdering(); if (relativeOrdering == null) { others.add(webFragment); return; } List after = relativeOrdering.getAfter(); List before = relativeOrdering.getBefore(); if (after.contains(OTHERS_TAG)) { afterOthers.add(webFragment); return; } if (before.contains(OTHERS_TAG)) { beforeOthers.add(webFragment); return; } others.add(webFragment); }); TopologicalSort topologicalSort = new TopologicalSort<>(); // Add the dependencies of each fragment Set referenced = new LinkedHashSet<>(); for (WebXml webFragment : webFragments) { if (webFragment.getRelativeOrdering() != null) { WebXml.RelativeOrder relativeOrdering = webFragment.getRelativeOrdering(); for (String afterName : relativeOrdering.getAfter()) { findWebFragment(afterName).ifPresent(fragment -> { topologicalSort.addDependency(webFragment, fragment); referenced.add(fragment); }); } for (String beforeName : relativeOrdering.getBefore()) { findWebFragment(beforeName).ifPresent(fragment -> { topologicalSort.addDependency(fragment, webFragment); referenced.add(fragment); }); } if (relativeOrdering.getBefore().contains(OTHERS_TAG)) { Consumer addBefore = other -> { if (!referenced.contains(other)) { topologicalSort.addDependency(other, webFragment); } }; others.forEach(addBefore); afterOthers.forEach(addBefore); } if (relativeOrdering.getAfter().contains(OTHERS_TAG)) { Consumer addAfter = other -> { if (!referenced.contains(other)) { topologicalSort.addDependency(webFragment, other); } }; beforeOthers.forEach(addAfter); others.forEach(addAfter); } } referenced.clear(); } return topologicalSort.sort(sorted); } private static class TopologicalSort { /** * Stores the dependencies. */ private final Map> dependencies = new HashMap<>(); public void addDependency(T dependant, T dependency) { Set set = dependencies.computeIfAbsent(dependant, k -> new LinkedHashSet<>()); Collections.addAll(set, dependency); } public List sort(List list) { Set visited = new HashSet<>(); List sorted = new ArrayList<>(); for (T item : list) { visit(item, visited, sorted); } return sorted; } private void visit(T item, Set visited, List sorted) { if (!visited.contains(item)) { visited.add(item); Set edges = this.dependencies.get(item); if (edges != null) edges.forEach(dependency -> visit(dependency, visited, sorted)); sorted.add(item); } else if (!sorted.contains(item)) throw new IllegalStateException("Invalid dependency cyclic"); } } } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlMimeMapping.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; /** * A mime-mapping inside of web.xml/web-fragment.xml. * * @param extension the extension. * @param mimeType the mime type. * @author Manfred Riem (mriem@manorrock.com) */ public record WebXmlMimeMapping(String extension, String mimeType) { } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlParser.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import static cloud.piranha.extension.webxml.internal.WebXml.OTHERS_TAG; import static java.lang.System.Logger.Level.DEBUG; import static java.lang.System.Logger.Level.WARNING; import static java.util.regex.Pattern.quote; import static javax.xml.xpath.XPathConstants.NODE; import static javax.xml.xpath.XPathConstants.NODESET; import java.io.InputStream; import java.lang.System.Logger; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.stream.StreamSupport; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathException; import javax.xml.xpath.XPathFactory; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * The web.xml / web-fragment.xml parser. * * @author Manfred Riem (mriem@manorrock.com) */ public class WebXmlParser { /** * Stores the 'location/text()' selector. */ private static final String LOCATION_TEXT_SELECTOR = "location/text()"; /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(WebXmlParser.class.getName()); /** * Stores the 'param-name/text()' selector. */ private static final String PARAM_NAME_TEXT_SELECTOR = "param-name/text()"; /** * Stores the 'servlet-name/text()' selector. */ private static final String SERVLET_NAME_TEXT_SELECTOR = "servlet-name/text()"; /** * Stores the 'text()' selector constant. */ private static final String TEXT_SELECTOR = "text()"; /** * Stores the 'url-pattern/text()' selector. */ private static final String URL_PATTERN_TEXT_SELECTOR = "url-pattern/text()"; /** * Parse the input stream. * * @param inputStream the input stream. * @return the WebXml. */ public WebXml parse(InputStream inputStream) { WebXml webXml = new WebXml(); try { DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document document = documentBuilder.parse(inputStream); XPath xPath = XPathFactory.newInstance().newXPath(); parseAbsoluteOrdering(webXml, xPath, document); parseOrdering(webXml, xPath, document); parseContextParameters(webXml, xPath, document); parseDefaultContextPath(webXml, xPath, document); parseDenyUncoveredHttpMethods(webXml, xPath, document); parseDisplayName(webXml, xPath, document); parseFragmentName(webXml, xPath, document); parseDistributable(webXml, xPath, document); parseErrorPages(webXml, xPath, document); parseFilters(webXml, xPath, document); parseFilterMappings(webXml, xPath, document); parseListeners(webXml, xPath, document); parseLoginConfig(webXml, xPath, document); parseMimeMappings(webXml, xPath, document); parseRequestCharacterEncoding(webXml, xPath, document); parseResponseCharacterEncoding(webXml, xPath, document); parseLocaleEncodingMapping(webXml, xPath, document); parseSecurityConstraints(webXml, xPath, document); parseSecurityRoles(webXml, xPath, document); parseServletMappings(webXml, xPath, document); parseServlets(webXml, xPath, document); parseSessionConfig(webXml, xPath, document); parseWebApp(webXml, xPath, document); parseWelcomeFiles(webXml, xPath, document); parseDataSources(webXml, xPath, document); parseJspConfig(webXml, xPath, document); } catch (Exception e) { LOGGER.log(WARNING, "Unable to parse web.xml", e); } return webXml; } private void parseAbsoluteOrdering(WebXml webXml, XPath xPath, Node rootNode) { try { Node absoluteOrderingNode = (Node) xPath.evaluate("//absolute-ordering", rootNode, NODE); if (absoluteOrderingNode == null) { return; } // It is possible to have only the to disable fragments List fragmentNames = new ArrayList<>(); NodeList childNodes = absoluteOrderingNode.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { Node item = childNodes.item(i); if ("others".equalsIgnoreCase(item.getNodeName())) { fragmentNames.add(OTHERS_TAG); continue; } String s = parseString(xPath, TEXT_SELECTOR, item); if (s != null && !s.trim().isEmpty()) { fragmentNames.add(s); } } webXml.setAbsoluteOrdering(fragmentNames); } catch (XPathException xpe) { LOGGER.log(WARNING, "Unable to parse section", xpe); } } /** * Parse a boolean. * * @param xPath the XPath to use. * @param node the node to use. * @param expression the expression to use. * @return the boolean. * @throws XPathException when an XPath error occurs. */ private static Boolean parseBoolean(XPath xPath, String expression, Node node) throws XPathException { return Boolean.parseBoolean((String) xPath .evaluate(expression, node, XPathConstants.STRING)); } /** * Parse the context-param sections. * * @param webXml the web.xml to add to. * @param xPath the XPath to use. * @param node the node to use. * @throws XPathException when an XPath error occurs. */ private void parseContextParameters(WebXml webXml, XPath xPath, Node node) throws XPathException { NodeList nodeList = (NodeList) xPath.evaluate("//context-param", node, NODESET); if (nodeList != null) { List contextParams = webXml.getContextParams(); for (int i = 0; i < nodeList.getLength(); i++) { String name = parseString(xPath, PARAM_NAME_TEXT_SELECTOR, nodeList.item(i)); String value = parseString(xPath, "param-value/text()", nodeList.item(i)); contextParams.add(new WebXmlContextParam(name, value)); } } } /** * Parse the data-source elements. * * @param webXml the webXml. * @param xPath the XPath. * @param node the DOM node. */ private void parseDataSources(WebXml webXml, XPath xPath, Document node) throws XPathException { List dataSources = webXml.getDataSources(); for (Node dataSourceNode : parseNodes(xPath, "//data-source", node)) { WebXmlDataSource dataSource = new WebXmlDataSource(); dataSource.setClassName(parseString(xPath, "class-name/text()", dataSourceNode)); dataSource.setName(parseString(xPath, "name/text()", dataSourceNode)); dataSource.setPassword(parseString(xPath, "password/text()", dataSourceNode)); dataSource.setUrl(parseString(xPath, "url/text()", dataSourceNode)); dataSource.setUser(parseString(xPath, "user/text()", dataSourceNode)); for (Node propertyNode : parseNodes(xPath, "property", dataSourceNode)) { dataSource.getProperties() .put( parseString(xPath, "name/text()", propertyNode), parseString(xPath, "value/text()", propertyNode)); } dataSources.add(dataSource); } } /** * Parse the default-context-path section. * * @param webXml the web.xml to add to. * @param xPath the XPath to use. * @param node the DOM node. * @throws XPathException when an XPath error occurs. */ private void parseDefaultContextPath(WebXml webXml, XPath xPath, Node node) throws XPathException { Node contextPathNode = (Node) xPath.evaluate("//default-context-path", node, NODE); if (contextPathNode != null) { String defaultContextPath = parseString(xPath, "//default-context-path/text()", node); if (defaultContextPath != null) { webXml.setDefaultContextPath(defaultContextPath); } } } /** * Parse the deny-uncovered-http-methods section. * * @param webXml the web.xml to add to. * @param xPath the XPath to use. * @param node the DOM node. * @throws XPathException when an XPath error occurs. */ private void parseDenyUncoveredHttpMethods(WebXml webXml, XPath xPath, Node node) throws XPathException { Node denyNode = (Node) xPath.evaluate("//deny-uncovered-http-methods", node, NODE); if (denyNode != null) { webXml.setDenyUncoveredHttpMethods(true); } } /** * Parse the display-name section. * * @param webXml the web.xml to add to. * @param xPath the XPath to use. * @param node the DOM node. * @throws XXPathException when an XPath error occurs. */ private void parseDisplayName(WebXml webXml, XPath xPath, Node node) throws XPathException { String displayName = parseString(xPath, "//display-name/text()", node); if (displayName != null) { webXml.setDisplayName(displayName); } } /** * Parse the distributable section. * * @param webXml the web.xml to add to. * @param xPath the XPath to use. * @param node the DOM node. * @throws XPathException when an XPath error occurs. */ private void parseDistributable(WebXml webXml, XPath xPath, Node node) throws XPathException { Node denyNode = (Node) xPath.evaluate("//distributable", node, NODE); if (denyNode != null) { webXml.setDistributable(true); } } /** * Parse the error-page sections. * * @param webXml the web.xml to add to. * @param xPath the XPath to use. * @param node the DOM node. * @throws XPathException when an XPath error occurs. */ private void parseErrorPages(WebXml webXml, XPath xPath, Node node) throws XPathException { NodeList nodeList = (NodeList) xPath.evaluate("//error-page", node, NODESET); if (nodeList != null) { List errorPages = webXml.getErrorPages(); for (int i = 0; i < nodeList.getLength(); i++) { String errorCode = parseString(xPath, "error-code/text()", nodeList.item(i)); String exceptionType = parseString(xPath, "exception-type/text()", nodeList.item(i)); String location = parseString(xPath, LOCATION_TEXT_SELECTOR, nodeList.item(i)); errorPages.add(new WebXmlErrorPage(errorCode, exceptionType, location)); } } } /** * Parse the filter-mapping sections. * * @param webXml the web.xml to use. * @param xPath the XPath to use. * @param rootNode the node to use. * @throws XPathException when an XPath error occurs. */ private void parseFilterMappings(WebXml webXml, XPath xPath, Node rootNode) throws XPathException { for (Node node : parseNodes(xPath, "//filter-mapping", rootNode)) { String filterName = parseString(xPath, "filter-name/text()", node); WebXmlFilterMapping webXmlFilterMapping = new WebXmlFilterMapping(); webXmlFilterMapping.setFilterName(filterName); for (String urlPattern : parseStrings(xPath, URL_PATTERN_TEXT_SELECTOR, node)) { webXmlFilterMapping.getUrlPatterns().add(urlPattern); } for (String servletName : parseStrings(xPath, SERVLET_NAME_TEXT_SELECTOR, node)) { webXmlFilterMapping.getServletNames().add(servletName); } for (String dispatcher : parseStrings(xPath, "dispatcher/text()", node)) { webXmlFilterMapping.getDispatchers().add(dispatcher); } webXml.getFilterMappings().add(webXmlFilterMapping); } } /** * Parse the filter sections. * * @param webXml the web.xml to add to. * @param xPath the XPath to use. * @param node the DOM node. * @throws XPathException when an XPath error occurs. */ private void parseFilters(WebXml webXml, XPath xPath, Node node) throws XPathException { NodeList nodeList = (NodeList) xPath.evaluate("//filter", node, NODESET); if (nodeList != null) { List filters = webXml.getFilters(); for (int i = 0; i < nodeList.getLength(); i++) { WebXmlFilter filter = new WebXmlFilter(); String filterName = parseString(xPath, "filter-name/text()", nodeList.item(i)); filter.setFilterName(filterName); String className = parseString(xPath, "filter-class/text()", nodeList.item(i)); filter.setClassName(className); String servletName = parseString(xPath, SERVLET_NAME_TEXT_SELECTOR, nodeList.item(i)); filter.setServletName(servletName); Boolean asyncSupported = parseBoolean(xPath, "async-supported/text()", nodeList.item(i)); if (asyncSupported != null) { filter.setAsyncSupported(asyncSupported); } filters.add(filter); NodeList paramNodeList = (NodeList) xPath.evaluate("init-param", nodeList.item(i), NODESET); for (int j = 0; j < paramNodeList.getLength(); j++) { String name = parseString(xPath, PARAM_NAME_TEXT_SELECTOR, paramNodeList.item(j)); String value = parseString(xPath, "param-value/text()", paramNodeList.item(j)); filter.addInitParam(new WebXmlFilterInitParam(name, value)); } } } } /** * Parse the name section of a fragment. * * @param webXml the web.xml to add to. * @param xPath the XPath to use. * @param node the DOM node. * @throws XPathException when an XPath error occurs. */ private void parseFragmentName(WebXml webXml, XPath xPath, Node node) throws XPathException { String fragmentName = parseString(xPath, "//name/text()", node); if (fragmentName != null) { webXml.setFragmentName(fragmentName); } } /** * Parse an integer. * * @param xPath the XPath to use. * @param expression the expression. * @param node the node. * @return the string. * @throws XPathException when the expression was invalid. */ private int parseInteger(XPath xPath, String expression, Node node) throws XPathException { Double doubleValue = (Double) xPath.evaluate(expression, node, XPathConstants.NUMBER); return doubleValue.intValue(); } /** * Parse the jsp-config element. * * @param webXml the webXml. * @param xPath the XPath. * @param node the DOM node. */ private void parseJspConfig(WebXml webXml, XPath xPath, Document node) throws XPathException { parseJspConfigTaglibs(webXml, xPath, node); } /** * Parse the jsp-config / taglib elements. * * @param webXml the webXml. * @param xPath the XPath. * @param node the DOM node. */ private void parseJspConfigTaglibs(WebXml webXml, XPath xPath, Document node) throws XPathException { NodeList nodeList = (NodeList) xPath.evaluate("//jsp-config", node, NODESET); if (nodeList != null) { List taglibs = webXml.getJspConfig().getTaglibs(); for (int i = 0; i < nodeList.getLength(); i++) { String location = parseString(xPath, "taglib/taglib-location/text()", nodeList.item(i)); String uri = parseString(xPath, "taglib/taglib-uri/text()", nodeList.item(i)); taglibs.add(new WebXmlJspConfigTaglib(location, uri)); } } } /** * Parse the listener sections. * * @param webXml the web.xml to add to. * @param xPath the XPath to use. * @param node the DOM node. * @throws XPathException when XPath error occurs. */ private void parseListeners(WebXml webXml, XPath xPath, Node node) throws XPathException { NodeList nodeList = (NodeList) xPath.evaluate("//listener", node, NODESET); if (nodeList != null) { List listeners = webXml.getListeners(); for (int i = 0; i < nodeList.getLength(); i++) { String className = parseString(xPath, "listener-class/text()", nodeList.item(i)); listeners.add(new WebXmlListener(className)); } } } /** * Parse the locale-encoding-mapping-list section. * * @param webXml the web.xml to add to. * @param xPath the XPath to use. * @param node the DOM node. * @throws XPathException when an XPath error occurs. */ private void parseLocaleEncodingMapping(WebXml webXml, XPath xPath, Node node) throws XPathException { NodeList nodeList = (NodeList) xPath.evaluate("//locale-encoding-mapping-list/locale-encoding-mapping", node, NODESET); if (nodeList != null) { Map localeEncodingMapping = webXml.getLocaleEncodingMapping(); for (int i = 0; i < nodeList.getLength(); i++) { String locale = parseString(xPath, ".//locale/text()", nodeList.item(i)); String encoding = parseString(xPath, ".//encoding/text()", nodeList.item(i)); localeEncodingMapping.put(locale, encoding); } } } /** * Parse the login-config section. * * @param webXml the web.xml to add to. * @param xPath the XPath to use. * @param node the DOM node. * @throws XPathException when an XPath error occurs. */ private void parseLoginConfig(WebXml webXml, XPath xPath, Node node) throws XPathException { Node configNode = (Node) xPath.evaluate("//login-config", node, NODE); if (configNode != null) { String authMethod = parseString(xPath, "//auth-method/text()", configNode); String realmName = parseString(xPath, "//realm-name/text()", configNode); String formLoginPage = parseString(xPath, "//form-login-config/form-login-page/text()", configNode); String formErrorPage = parseString(xPath, "//form-login-config/form-error-page/text()", configNode); WebXmlLoginConfig config = new WebXmlLoginConfig( authMethod, realmName, formLoginPage, formErrorPage); webXml.setLoginConfig(config); } } /** * Parse a long. * * @param xPath the XPath to use. * @param node the node to use. * @param expression the expression to use. * @return the long. */ private static Long parseLong(XPath xPath, String expression, Node node) throws XPathException { return Long.parseLong((String) xPath.evaluate(expression, node, XPathConstants.STRING)); } /** * Parse the mime-mapping sections. * * @param webXml the web.xml to add to. * @param xPath the XPath to use. * @param node the node to use. * @throws XPathException when an XPath error occurs. */ private void parseMimeMappings(WebXml webXml, XPath xPath, Node node) throws XPathException { NodeList nodeList = (NodeList) xPath.evaluate("//mime-mapping", node, NODESET); if (nodeList != null) { List mimeMappings = webXml.getMimeMappings(); for (int i = 0; i < nodeList.getLength(); i++) { String extension = parseString(xPath, "//extension/text()", nodeList.item(i)); String mimeType = parseString(xPath, "//mime-type/text()", nodeList.item(i)); mimeMappings.add(new WebXmlMimeMapping(extension, mimeType)); } } } /** * Parse nodes. * * @param xPath the XPath to use. * @param expression the expression. * @param node the node to use. * @throws XPathException when an XPath error occurs. * @return iterable nodes. */ private Iterable parseNodes(XPath xPath, String expression, Node node) throws XPathException { return StreamSupport .stream(toIterable((NodeList) xPath.evaluate(expression, node, NODESET)).spliterator(), false)::iterator; } private void parseOrdering(WebXml webXml, XPath xPath, Node node) { try { NodeList before = (NodeList) xPath.evaluate("//ordering/before", node, NODESET); if (before.getLength() > 1) { throw new IllegalStateException("Cannot have multiple tags in "); } NodeList after = (NodeList) xPath.evaluate("//ordering/after", node, NODESET); if (after.getLength() > 1) { throw new IllegalStateException("Cannot have multiple tags in "); } List beforeValues = parseOrderingChildren(xPath, before); List afterValues = parseOrderingChildren(xPath, after); if (!beforeValues.isEmpty() || !afterValues.isEmpty()) { webXml.setRelativeOrdering(new WebXml.RelativeOrder(beforeValues, afterValues)); } } catch (Exception xpe) { LOGGER.log(WARNING, "Unable to parse section", xpe); } } private List parseOrderingChildren(XPath xPath, NodeList orderingChild) throws XPathException { List values = new ArrayList<>(); if (orderingChild.getLength() == 1) { Node beforeTag = orderingChild.item(0); for (Node orderingNode : parseNodes(xPath, "*", beforeTag)) { String fragmentName = parseString(xPath, TEXT_SELECTOR, orderingNode); if (fragmentName != null && !fragmentName.trim().isEmpty()) { values.add(fragmentName); continue; } if ("others".equalsIgnoreCase(orderingNode.getNodeName())) { values.add(OTHERS_TAG); } } } return values; } /** * Parse the request-character-encoding section. * * @param webXml the web.xml to add to. * @param xPath the XPath to use. * @param node the DOM node. * @throws XPathException when an XPath error occurs. */ private void parseRequestCharacterEncoding(WebXml webXml, XPath xPath, Node node) throws XPathException { Node rceNode = (Node) xPath.evaluate("//request-character-encoding", node, NODE); if (rceNode != null) { String requestCharacterEncoding = parseString( xPath, "//request-character-encoding/text()", node); webXml.setRequestCharacterEncoding(requestCharacterEncoding); } } /** * Parse the response-character-encoding section. * * @param webXml the web.xml to add to. * @param xPath the XPath to use. * @param node the DOM node. * @throws XPathException when an XPath error occurs. */ private void parseResponseCharacterEncoding(WebXml webXml, XPath xPath, Node node) throws XPathException { Node rceNode = (Node) xPath.evaluate("//response-character-encoding", node, NODE); if (rceNode != null) { String responseCharacterEncoding = parseString( xPath, "//response-character-encoding/text()", node); webXml.setResponseCharacterEncoding(responseCharacterEncoding); } } private void parseSecurityConstraint(WebXml webXml, XPath xPath, Node rootNode) throws XPathException { WebXmlSecurityConstraint securityConstraint = new WebXmlSecurityConstraint(); for (Node node : parseNodes(xPath, "web-resource-collection", rootNode)) { WebXmlSecurityConstraint.WebResourceCollection webResourceCollection = new WebXmlSecurityConstraint.WebResourceCollection(); for (String urlPattern : parseStrings(xPath, URL_PATTERN_TEXT_SELECTOR, node)) { webResourceCollection.getUrlPatterns().add(urlPattern); } for (String httpMethod : parseStrings(xPath, "http-method/text()", node)) { webResourceCollection.getHttpMethods().add(httpMethod); } for (String httpMethodOmission : parseStrings(xPath, "http-method-omission/text()", node)) { webResourceCollection.getHttpMethodOmissions().add(httpMethodOmission); } securityConstraint.getWebResourceCollections().add(webResourceCollection); } for (Node node : parseNodes(xPath, "auth-constraint", rootNode)) { for (String roleName : parseStrings(xPath, "role-name/text()", node)) { securityConstraint.getRoleNames().add(roleName); } } securityConstraint.setTransportGuarantee(parseString(xPath, "user-data-constraint/transport-guarantee/text()", rootNode)); webXml.getSecurityConstraints().add(securityConstraint); } /** * Parse the security constraints. * * @param webXml the web.xml to add to. * @param xPath the XPath to use. * @param node the DOM node. * @throws XPathException when an XPath error occurs. */ private void parseSecurityConstraints(WebXml webXml, XPath xPath, Node node) throws XPathException { for (Node item : parseNodes(xPath, "//security-constraint", node)) { parseSecurityConstraint(webXml, xPath, item); } } private void parseSecurityRoles(WebXml webXml, XPath xPath, Node rootNode) throws XPathException { for (String roleName : parseStrings(xPath, "//security-role/role-name/text()", rootNode)) { webXml.getRoleNames().add(roleName); } } /** * Parse the servlet-mapping sections. * * @param webXml the web.xml to use. * @param xPath the XPath to use. * @param node the node to use. * @throws XPathException when an XPath error occurs. */ private void parseServletMappings(WebXml webXml, XPath xPath, Node node) throws XPathException { NodeList nodeList = (NodeList) xPath.evaluate("//servlet-mapping", node, NODESET); if (nodeList != null) { List servletMappings = webXml.getServletMappings(); for (int i = 0; i < nodeList.getLength(); i++) { String servletName = parseString(xPath, SERVLET_NAME_TEXT_SELECTOR, nodeList.item(i)); for (String urlPattern : parseStrings(xPath, URL_PATTERN_TEXT_SELECTOR, nodeList.item(i))) { servletMappings.add(new WebXmlServletMapping(servletName, urlPattern)); } } } } /** * Parse the servlet sections. * * @param webXml the web.xml to add to. * @param xPath the XPath to use. * @param rootNode the DOM node. * @throws XPathException when a XPath error occurs. */ private void parseServlets(WebXml webXml, XPath xPath, Node rootNode) throws XPathException { List servlets = webXml.getServlets(); for (Node servletNode : parseNodes(xPath, "//servlet", rootNode)) { WebXmlServlet servlet = new WebXmlServlet(); servlet.setServletName(parseString(xPath, SERVLET_NAME_TEXT_SELECTOR, servletNode)); servlet.setClassName(parseString(xPath, "servlet-class/text()", servletNode)); servlet.setJspFile(parseString(xPath, "jsp-file/text()", servletNode)); Boolean asyncSupported = parseBoolean(xPath, "async-supported/text()", servletNode); if (asyncSupported != null) { servlet.setAsyncSupported(asyncSupported); } for (Node initParamNode : parseNodes(xPath, "init-param", servletNode)) { String name = parseString(xPath, PARAM_NAME_TEXT_SELECTOR, initParamNode); String value = parseString(xPath, "param-value/text()", initParamNode); servlet.getInitParams().add(new WebXmlServletInitParam(name, value)); } for (Node securityRoleRefNode : parseNodes(xPath, "security-role-ref", servletNode)) { String roleName = parseString(xPath, "role-name/text()", securityRoleRefNode); String roleLink = parseString(xPath, "role-link/text()", securityRoleRefNode); servlet.getSecurityRoleRefs().add(new WebXmlServletSecurityRoleRef(roleName, roleLink)); } for (Node multipartConfigNode : parseNodes(xPath, "multipart-config", servletNode)) { if (servlet.getMultipartConfig() != null) { LOGGER.log(WARNING, "Duplicate sections in web.xml where only 1 allowed."); break; } WebXmlServletMultipartConfig multipartConfig = new WebXmlServletMultipartConfig(); multipartConfig.setLocation(parseString(xPath, LOCATION_TEXT_SELECTOR, multipartConfigNode)); multipartConfig.setMaxFileSize(parseLong(xPath, LOCATION_TEXT_SELECTOR, multipartConfigNode)); multipartConfig.setMaxRequestSize(parseLong(xPath, LOCATION_TEXT_SELECTOR, multipartConfigNode)); multipartConfig.setFileSizeThreshold(parseInteger(xPath, LOCATION_TEXT_SELECTOR, multipartConfigNode)); servlet.setMultipartConfig(multipartConfig); } servlets.add(servlet); LOGGER.log(DEBUG, "Configured servlet: {0}", servlet); } } /** * Parse the session-config section. * * @param webXml the web.xml to add to. * @param xPath the XPath to use. * @param node the DOM node. * @throws XPathException when an XPath error occurs. */ private void parseSessionConfig(WebXml webXml, XPath xPath, Node node) throws XPathException { Node scNode = (Node) xPath.evaluate("//session-config", node, NODE); if (scNode != null) { int sessionTimeout = parseInteger(xPath, "session-timeout/text()", scNode); WebXmlSessionConfig sessionConfig = new WebXmlSessionConfig(); sessionConfig.setSessionTimeout(sessionTimeout); webXml.setSessionConfig(sessionConfig); } } /** * Parse a string. * * @param xPath the XPath to use. * @param expression the expression. * @param node the node. * @return the string. * @throws XPathException when the expression was invalid. */ private String parseString(XPath xPath, String expression, Node node) throws XPathException { return (String) xPath.evaluate(expression, node, XPathConstants.STRING); } private Iterable parseStrings(XPath xPath, String expression, Node node) throws XPathException { return StreamSupport .stream(toIterable((NodeList) xPath.evaluate(expression, node, NODESET)).spliterator(), false) .map(Node::getNodeValue)::iterator; } /** * Parse the web-app section. * * @param webXml the web.xml to add to. * @param xPath the XPath to use. * @param node the DOM node. * @throws XPathException when an XPath error occurs. */ private void parseWebApp(WebXml webXml, XPath xPath, Node node) throws XPathException { Node webAppNode = (Node) xPath.evaluate("//web-app", node, NODE); if (webAppNode != null) { NamedNodeMap attributes = webAppNode.getAttributes(); if (attributes != null) { Node versionNode = attributes.getNamedItem("version"); if (versionNode != null) { String version = versionNode.getTextContent(); if (version != null) { String[] versionComponents = version.split(quote(".")); if (versionComponents.length > 0) { webXml.setMajorVersion(Integer.valueOf(versionComponents[0])); } if (versionComponents.length > 1) { webXml.setMinorVersion(Integer.valueOf(versionComponents[1])); } } } Node metadataCompleteNode = attributes.getNamedItem("metadata-complete"); if (metadataCompleteNode != null) { webXml.setMetadataComplete(Boolean.parseBoolean(metadataCompleteNode.getTextContent())); } } } } /** * Convert nodes list to iterable nodes. * * @param nodeList the node list. * @return the iterable nodes. */ public static Iterable toIterable(NodeList nodeList) { return () -> new Iterator() { private int position; @Override public boolean hasNext() { return position < nodeList.getLength(); } @Override public Node next() { if (hasNext()) { return nodeList.item(position++); } throw new NoSuchElementException(); } }; } /** * Parse the welcome file section. * * @param webXml the web.xml to add to. * @param xPath the XPath to use. * @param node the DOM node. */ private void parseWelcomeFiles(WebXml webXml, XPath xPath, Node node) { try { NodeList nodeList = (NodeList) xPath.evaluate("//welcome-file-list/welcome-file", node, NODESET); if (nodeList != null) { List welcomeFiles = webXml.getWelcomeFiles(); for (int i = 0; i < nodeList.getLength(); i++) { String welcomeFile = parseString(xPath, TEXT_SELECTOR, nodeList.item(i)); welcomeFiles.add(welcomeFile); LOGGER.log(DEBUG, "Parsed welcome-file: {0}", welcomeFile); } } } catch (XPathException xpe) { LOGGER.log(WARNING, "Unable to parse sections", xpe); } } } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlProcessor.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import static java.lang.System.Logger.Level.DEBUG; import static java.lang.System.Logger.Level.TRACE; import static java.lang.System.Logger.Level.WARNING; import static java.util.stream.Collectors.toSet; import java.lang.System.Logger; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.EnumSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.CommonDataSource; import cloud.piranha.core.api.ErrorPageManager; import cloud.piranha.core.api.LocaleEncodingManager; import cloud.piranha.core.api.SecurityConstraint; import cloud.piranha.core.api.SecurityManager; import cloud.piranha.core.api.SecurityRoleReference; import cloud.piranha.core.api.SecurityWebResourceCollection; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WelcomeFileManager; import cloud.piranha.core.impl.DefaultJspConfigDescriptor; import cloud.piranha.core.impl.DefaultTaglibDescriptor; import jakarta.servlet.DispatcherType; import jakarta.servlet.FilterRegistration; import jakarta.servlet.MultipartConfigElement; import jakarta.servlet.ServletRegistration; import jakarta.servlet.descriptor.JspConfigDescriptor; import java.util.ArrayList; /** * The web.xml / web-fragment.xml processor. * * @author Manfred Riem (mriem@manorrock.com) */ public class WebXmlProcessor { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(WebXmlProcessor.class.getName()); /** * Stores the empty string array. */ private static final String[] STRING_ARRAY = new String[0]; /** * Process the web.xml into the web application. * * @param webXml the web.xml * @param webApplication the web application. */ public void process(WebXml webXml, WebApplication webApplication) { LOGGER.log(TRACE, "Started WebXmlProcessor.process"); processMetadataComplete(webApplication, webXml); processContextParameters(webApplication, webXml); processDefaultContextPath(webApplication, webXml); processDenyUncoveredHttpMethods(webApplication, webXml); processDisplayName(webApplication, webXml); processDistributable(webApplication, webXml); processErrorPages(webApplication, webXml); processFilters(webApplication, webXml); processFilterMappings(webApplication, webXml); processListeners(webApplication, webXml); processLoginConfig(webApplication, webXml); processMimeMappings(webApplication, webXml); processRequestCharacterEncoding(webApplication, webXml); processResponseCharacterEncoding(webApplication, webXml); processRoleNames(webApplication, webXml); processSecurityConstraints(webApplication, webXml); processServlets(webApplication, webXml); processServletMappings(webApplication, webXml); processWebApp(webApplication, webXml); processWelcomeFiles(webApplication, webXml); processLocaleEncodingMapping(webApplication, webXml); processSessionConfig(webApplication, webXml); processDataSources(webApplication, webXml); processJspConfig(webApplication, webXml); LOGGER.log(TRACE, "Finished WebXmlProcessor.process"); } /** * Process the context parameters. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processContextParameters(WebApplication webApplication, WebXml webXml) { for (WebXmlContextParam contextParam : webXml.getContextParams()) { webApplication.setInitParameter(contextParam.name(), contextParam.value()); } } /** * Process the data sources. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processDataSources(WebApplication webApplication, WebXml webXml) { for (WebXmlDataSource dataSourceXml : webXml.getDataSources()) { try { CommonDataSource dataSource = (CommonDataSource) Class.forName(dataSourceXml.getClassName()) .getDeclaredConstructor() .newInstance(); if (dataSourceXml.getUrl() != null) { dataSource.getClass() .getMethod("setUrl", String.class) .invoke(dataSource, dataSourceXml.getUrl()); } if (dataSourceXml.getPassword() != null) { dataSource.getClass() .getMethod("setPassword", String.class) .invoke(dataSource, dataSourceXml.getPassword()); } if (dataSourceXml.getUser() != null) { dataSource.getClass() .getMethod("setUser", String.class) .invoke(dataSource, dataSourceXml.getUser()); } if (!dataSourceXml.getProperties().isEmpty()) { Method[] dataSourceMethods = dataSource.getClass().getMethods(); properties: for (var property : dataSourceXml.getProperties().entrySet()) { for (Method dataSourceMethod : dataSourceMethods) { if (isStringSetter(dataSourceMethod)) { String methodName = "set" + capitalize(property.getKey()); if (dataSourceMethod.getName().equals(methodName)) { dataSourceMethod.invoke(dataSource, property.getValue()); break properties; } } } } } InitialContext initialContext = new InitialContext(); initialContext.bind(dataSourceXml.getName(), dataSource); } catch (ClassNotFoundException | IllegalAccessException | IllegalArgumentException | InstantiationException | NoSuchMethodException | SecurityException | InvocationTargetException | NamingException e) { LOGGER.log(WARNING, "Unable to create DataSource", e); } } } private boolean isStringSetter(Method method) { return method.getParameterCount() == 1 && method.getParameters()[0].getType().equals(String.class); } private String capitalize(String string) { if (string == null || string.isEmpty()) { return string; } return string.substring(0, 1).toUpperCase() + string.substring(1); } /** * Process the default context path. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processDefaultContextPath(WebApplication webApplication, WebXml webXml) { if (webXml.getDefaultContextPath() != null) { webApplication.setContextPath(webXml.getDefaultContextPath()); } } /** * Process the metadata complete. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processMetadataComplete(WebApplication webApplication, WebXml webXml) { if (webXml.getMetadataComplete()) { webApplication.setMetadataComplete(true); } } /** * Process the deny uncovered HTTP methods flag. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processDenyUncoveredHttpMethods(WebApplication webApplication, WebXml webXml) { if (webApplication.getManager().getSecurityManager() != null) { webApplication.getManager().getSecurityManager() .setDenyUncoveredHttpMethods(webXml.getDenyUncoveredHttpMethods()); } } /** * Process the display name flag. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processDisplayName(WebApplication webApplication, WebXml webXml) { webApplication.setServletContextName(webXml.getDisplayName()); } /** * Process the distributable flag. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processDistributable(WebApplication webApplication, WebXml webXml) { webApplication.setDistributable(webXml.isDistributable()); } /** * Process the error pages. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processErrorPages(WebApplication webApplication, WebXml webXml) { if (webApplication.getManager().getErrorPageManager() != null) { ErrorPageManager errorPageManager = webApplication.getManager().getErrorPageManager(); for (WebXmlErrorPage errorPage : webXml.getErrorPages()) { if (errorPage.errorCode() != null && !errorPage.errorCode().isEmpty()) { errorPageManager.addErrorPage(Integer.parseInt(errorPage.errorCode()), errorPage.location()); } else if (errorPage.exceptionType() != null && !errorPage.exceptionType().isEmpty()) { errorPageManager.addErrorPage(errorPage.exceptionType(), errorPage.location()); } } } } /** * Process the filter mappings mappings. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processFilterMappings(WebApplication webApplication, WebXml webXml) { webXml.getFilterMappings().forEach(filterMapping -> { FilterRegistration filterReg = webApplication .getFilterRegistration(filterMapping.getFilterName()); // Filter is mapped to a URL pattern, e.g. /path/customer filterReg.addMappingForUrlPatterns( toEnumDispatcherTypes(filterMapping.getDispatchers()), true, filterMapping.getUrlPatterns().toArray(STRING_ARRAY)); // Filter is mapped to a named Servlet, e.g. FacesServlet webApplication.addFilterMapping( toDispatcherTypes(filterMapping.getDispatchers()), filterMapping.getFilterName(), true, filterMapping.getServletNames().stream().map(e -> "servlet:// " + e).toArray(String[]::new)); }); } @SuppressWarnings("unchecked") private EnumSet toEnumDispatcherTypes(List dispatchers) { if (dispatchers == null) { return null; } @SuppressWarnings("rawtypes") EnumSet enumSet = EnumSet.noneOf(DispatcherType.class); for (String dispatcherType : dispatchers) { enumSet.add(DispatcherType.valueOf(dispatcherType)); } return enumSet; } private Set toDispatcherTypes(List dispatchers) { if (dispatchers == null) { return null; } return dispatchers.stream() .map(DispatcherType::valueOf) .collect(toSet()); } /** * Process the filters. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processFilters(WebApplication webApplication, WebXml webXml) { webXml.getFilters().forEach(filter -> { FilterRegistration.Dynamic dynamic = null; if (filter.getClassName() != null) { dynamic = webApplication.addFilter(filter.getFilterName(), filter.getClassName()); } else if (filter.getServletName() != null) { dynamic = webApplication.addFilter(filter.getFilterName(), filter.getServletName()); } if (dynamic != null && filter.isAsyncSupported()) { dynamic.setAsyncSupported(true); } if (dynamic != null) { for (WebXmlFilterInitParam initParam : filter.getInitParams()) { dynamic.setInitParameter(initParam.name(), initParam.value()); } } }); } /** * Process the jsp config. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processJspConfig(WebApplication webApplication, WebXml webXml) { List taglibs = webXml.getJspConfig().getTaglibs(); if (!taglibs.isEmpty()) { JspConfigDescriptor descriptor = webApplication.getJspConfigDescriptor(); if (descriptor == null) { descriptor = new DefaultJspConfigDescriptor(); webApplication.setJspConfigDescriptor(descriptor); } for (WebXmlJspConfigTaglib taglib : taglibs) { DefaultTaglibDescriptor taglibDescriptor = new DefaultTaglibDescriptor(); taglibDescriptor.setTaglibLocation(taglib.getLocation()); taglibDescriptor.setTaglibURI(taglib.getUri()); descriptor.getTaglibs().add(taglibDescriptor); } } } /** * Process the listeners. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processListeners(WebApplication webApplication, WebXml webXml) { for (WebXmlListener listener : webXml.getListeners()) { webApplication.addListener(listener.className()); } } /** * Process the login-config. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processLoginConfig(WebApplication webApplication, WebXml webXml) { SecurityManager manager = webApplication.getManager().getSecurityManager(); if (manager != null && webXml.getLoginConfig() != null) { WebXmlLoginConfig loginConfig = webXml.getLoginConfig(); if (loginConfig.authMethod() != null) { manager.setAuthMethod(loginConfig.authMethod()); } if (loginConfig.formErrorPage() != null) { manager.setFormErrorPage(loginConfig.formErrorPage()); } if (loginConfig.formLoginPage() != null) { manager.setFormLoginPage(loginConfig.formLoginPage()); } if (loginConfig.realmName() != null) { manager.setRealmName(loginConfig.realmName()); } } } /** * Process the mime mappings. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processMimeMappings(WebApplication webApplication, WebXml webXml) { webXml.getMimeMappings().forEach(mapping -> webApplication.addMimeType(mapping.extension(), mapping.mimeType()) ); } /** * Process the request character encoding. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processRequestCharacterEncoding(WebApplication webApplication, WebXml webXml) { if (webXml.getRequestCharacterEncoding() != null) { webApplication.setRequestCharacterEncoding(webXml.getRequestCharacterEncoding()); } } /** * Process the response character encoding. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processResponseCharacterEncoding(WebApplication webApplication, WebXml webXml) { if (webXml.getResponseCharacterEncoding() != null) { webApplication.setResponseCharacterEncoding(webXml.getResponseCharacterEncoding()); } } private void processRoleNames(WebApplication webApplication, WebXml webXml) { if (webApplication.getManager().getSecurityManager() != null) { webApplication.getManager().getSecurityManager().declareRoles(webXml.getRoleNames()); } } /** * Process the servlet mappings. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processServletMappings(WebApplication webApplication, WebXml webXml) { for (WebXmlServletMapping mapping : webXml.getServletMappings()) { // The application is allowed to override the *.jsp mapping set by a JSP implementation. if (mapping.urlPattern().equals("*.jsp")) { LOGGER.log(DEBUG, "Application defined *.jsp mapping, therefor overriding any existing mapping."); String oldServlet = webApplication.removeServletMapping(mapping.urlPattern()); if (oldServlet != null) { LOGGER.log(DEBUG, "The mapping for Servlet " + oldServlet + " for *.jsp has been overriden by mapping.servletName()"); } } webApplication.addServletMapping(mapping.servletName(), mapping.urlPattern()); } } /** * Process the web app. This is basically only the version contained within * it. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processWebApp(WebApplication webApplication, WebXml webXml) { if (webXml.getMajorVersion() != -1) { webApplication.setEffectiveMajorVersion(webXml.getMajorVersion()); } if (webXml.getMinorVersion() != -1) { webApplication.setEffectiveMinorVersion(webXml.getMinorVersion()); } } /** * Process the servlets. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processServlets(WebApplication webApplication, WebXml webXml) { LOGGER.log(DEBUG, "Configuring Servlets"); for (WebXmlServlet servlet : webXml.getServlets()) { LOGGER.log(DEBUG, () -> "Configuring Servlet: " + servlet.getServletName()); ServletRegistration.Dynamic dynamic; String jspFile = servlet.getJspFile(); if (!isEmpty(jspFile)) { dynamic = webApplication.addJspFile(servlet.getServletName(), jspFile); } else { dynamic = webApplication.addServlet(servlet.getServletName(), servlet.getClassName()); } if (servlet.isAsyncSupported()) { dynamic.setAsyncSupported(true); } WebXmlServletMultipartConfig multipartConfig = servlet.getMultipartConfig(); if (multipartConfig != null) { dynamic.setMultipartConfig( new MultipartConfigElement( multipartConfig.getLocation(), multipartConfig.getMaxFileSize(), multipartConfig.getMaxRequestSize(), multipartConfig.getFileSizeThreshold())); } servlet.getInitParams().forEach(initParam -> { ServletRegistration servletRegistration = webApplication.getServletRegistration(servlet.getServletName()); if (servletRegistration != null) { servletRegistration.setInitParameter(initParam.name(), initParam.value()); } }); SecurityManager securityManager = webApplication.getManager().getSecurityManager(); for(WebXmlServletSecurityRoleRef roleRef : servlet.getSecurityRoleRefs()) { String servletName = servlet.getServletName(); List roleReferences = securityManager.getSecurityRoleReferences().get(servletName); if (roleReferences == null) { roleReferences = new ArrayList<>(); securityManager.getSecurityRoleReferences().put(servletName, roleReferences); } SecurityRoleReference roleReference = new SecurityRoleReference(); roleReference.setRoleName(roleRef.roleName()); roleReference.setRoleLink(roleRef.roleLink()); roleReferences.add(roleReference); } LOGGER.log(DEBUG, () -> "Configured Servlet: " + servlet.getServletName()); } } /** * Process the welcome files. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processWelcomeFiles(WebApplication webApplication, WebXml webXml) { LOGGER.log(DEBUG, "Adding welcome files"); Iterator iterator = webXml.getWelcomeFiles().iterator(); WelcomeFileManager welcomeFileManager = webApplication.getManager().getWelcomeFileManager(); if (welcomeFileManager != null) { while (iterator.hasNext()) { String welcomeFile = iterator.next(); LOGGER.log(DEBUG, () -> "Adding welcome file: " + welcomeFile); welcomeFileManager.addWelcomeFile(welcomeFile); } } } /** * Process the locale-encoding mapping. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processLocaleEncodingMapping(WebApplication webApplication, WebXml webXml) { Map localeMapping = webXml.getLocaleEncodingMapping(); if (localeMapping != null) { LocaleEncodingManager localeEncodingManager = webApplication.getManager().getLocaleEncodingManager(); if (localeEncodingManager != null) { localeMapping.forEach(localeEncodingManager::addCharacterEncoding); } } } /** * Is the string empty. * * @param string the string. * @return true if it is, false otherwise. */ private boolean isEmpty(String string) { return string == null || string.isEmpty(); } /** * Process the session config. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processSessionConfig(WebApplication webApplication, WebXml webXml) { WebXmlSessionConfig sessionConfig = webXml.getSessionConfig(); if (sessionConfig != null) { webApplication.setSessionTimeout(sessionConfig.getSessionTimeout()); } } /** * Process the security constraints. * * @param webApplication the web application. * @param webXml the web.xml. */ private void processSecurityConstraints(WebApplication webApplication, WebXml webXml) { SecurityManager securityManager = webApplication.getManager().getSecurityManager(); for(WebXmlSecurityConstraint constraint : webXml.getSecurityConstraints()) { SecurityConstraint securityConstraint = new SecurityConstraint(); securityConstraint.getRoleNames().addAll(constraint.getRoleNames()); securityConstraint.setTransportGuarantee(constraint.getTransportGuarantee()); for(WebXmlSecurityConstraint.WebResourceCollection collection : constraint.getWebResourceCollections()) { SecurityWebResourceCollection swrc = new SecurityWebResourceCollection(); swrc.getHttpMethodOmissions().addAll(collection.getHttpMethodOmissions()); swrc.getHttpMethods().addAll(collection.getHttpMethods()); swrc.getUrlPatterns().addAll(collection.getUrlPatterns()); securityConstraint.getSecurityWebResourceCollections().add(swrc); } securityManager.getSecurityConstraints().add(securityConstraint); } } } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlSecurityConstraint.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import java.util.ArrayList; import java.util.List; /** * A security-constraint inside of web.xml/web-fragment.xml. * * @author Manfred Riem (mriem@manorrock.com) */ public class WebXmlSecurityConstraint { /** * The list <role-name> snippets inside <auth-constraint> * * Note that we don't map the <auth-constraint> element separately * here */ private List roleNames = new ArrayList<>(); /** * The list <transport-guarantee> snippet inside * <user-data-constraint> * * Note that we don't map the <user-data-constraint> element * separately here */ private String transportGuarantee; // Example: // // // SecureServlet // /SecureServlet // GET // POST // // // someRole // // // NONE // // /** * Stores the web resource collections. */ private List webResourceCollections = new ArrayList<>(); /** * Constructor. */ public WebXmlSecurityConstraint() { } /** * Get the role names. * * @return the role names. */ public List getRoleNames() { return roleNames; } /** * Get the transport guarantee. * * @return the transport guarantee. */ public String getTransportGuarantee() { return transportGuarantee; } /** * Get the web resource collections. * * @return the web resource collections. */ public List getWebResourceCollections() { return webResourceCollections; } /** * Set the role names. * * @param roleNames the role names. */ public void setRoleNames(List roleNames) { this.roleNames = roleNames; } /** * Set the transport guarantee. * * @param transportGuarantee the transport guarantee. */ public void setTransportGuarantee(String transportGuarantee) { this.transportGuarantee = transportGuarantee; } /** * Set the web resource collections. * * @param webResourceCollections the web resource collections. */ public void setWebResourceCollections(List webResourceCollections) { this.webResourceCollections = webResourceCollections; } /** * The <web-resource-collection> snippet inside a web.xml / * webfragment.xml. */ public static class WebResourceCollection { /** * The list <url-pattern> snippets inside * <web-resource-collection> */ private List urlPatterns = new ArrayList<>(); /** * The list <http-method> snippets inside * <web-resource-collection> */ private List httpMethods = new ArrayList<>(); /** * The list <http-method-omission> snippets inside * <web-resource-collection> */ private List httpMethodOmissions = new ArrayList<>(); /** * Default constructor. */ public WebResourceCollection() { } /** * Get the URL patterns. * * @return the URL patterns. */ public List getUrlPatterns() { return urlPatterns; } /** * Set the URL patterns. * * @param urlPatterns the URL patterns. */ public void setUrlPatterns(List urlPatterns) { this.urlPatterns = urlPatterns; } /** * Get the HTTP methods. * * @return the HTTP methods. */ public List getHttpMethods() { return httpMethods; } /** * Set the HTTP methods. * * @param httpMethods the HTTP methods. */ public void setHttpMethods(List httpMethods) { this.httpMethods = httpMethods; } /** * Get the HTTP method omissions. * * @return the HTTP method omissions. */ public List getHttpMethodOmissions() { return httpMethodOmissions; } /** * Set the HTTP method omissions. * * @param httpMethodOmissions the HTTP method omissions. */ public void setHttpMethodOmissions(List httpMethodOmissions) { this.httpMethodOmissions = httpMethodOmissions; } } } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import java.util.ArrayList; import java.util.List; /** * A servlet inside of web.xml/web-fragment.xml. * * @author Manfred Riem (mriem@manorrock.com) */ public class WebXmlServlet { /** * Stores if async is supported. */ private boolean asyncSupported; /** * Stores the class name. */ private String className; /** * Stores the init params. */ private final List initParams = new ArrayList<>(); /** * Stores the JSP file */ private String jspFile; /** * Stores the MultipartConfig */ private WebXmlServletMultipartConfig multipartConfig; /** * Stores the security role refs. */ private final List securityRoleRefs = new ArrayList<>(); /** * Stores the servlet name. */ private String servletName; /** * Default constructor. */ public WebXmlServlet() { } /** * Add init param. * * @param initParam the init param. */ public void addInitParam(WebXmlServletInitParam initParam) { this.initParams.add(initParam); } /** * Get the class name. * * @return the class name. */ public String getClassName() { return className; } /** * Get the init parameters. * * @return the init parameters. */ public List getInitParams() { return initParams; } /** * Get the JSP file. * * @return the JSP file. */ public String getJspFile() { return jspFile; } /** * Get the multipart config. * * @return the multipart config. */ public WebXmlServletMultipartConfig getMultipartConfig() { return multipartConfig; } /** * Get the security role refs. * * @return the security role refs. */ public List getSecurityRoleRefs() { return securityRoleRefs; } /** * Get the servlet name. * * @return the servlet name. */ public String getServletName() { return servletName; } /** * Is async supported. * * @return true if it is, false otherwise. */ public boolean isAsyncSupported() { return asyncSupported; } /** * Set if async is supported. * * @param asyncSupported the boolean value. */ public void setAsyncSupported(boolean asyncSupported) { this.asyncSupported = asyncSupported; } /** * Set the class name. * * @param className the class name. */ public void setClassName(String className) { this.className = className; } /** * Set the JSP file. * * @param jspFile the JSP file. */ public void setJspFile(String jspFile) { this.jspFile = jspFile; } /** * Set the multipart config. * * @param multipartConfig the multipart config. */ public void setMultipartConfig(WebXmlServletMultipartConfig multipartConfig) { this.multipartConfig = multipartConfig; } /** * Set the servlet name. * * @param servletName the servlet name. */ public void setServletName(String servletName) { this.servletName = servletName; } /** * Get the string representation. * * @return the string representation. */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Servlet["); builder.append("servletName=").append(servletName).append(","); builder.append("className=").append(className).append(","); builder.append("jspFile=").append(jspFile).append(","); builder.append("asyncSupported=").append(asyncSupported).append("]"); return builder.toString(); } } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlServletInitParam.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; /** * A servlet init-param inside of web.xml/web-fragment.xml. * * @param name the name. * @param value the value. * @author Manfred Riem (mriem@manorrock.com) */ public record WebXmlServletInitParam(String name, String value) { } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlServletMapping.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; /** * A servlet-mapping inside of web.xml/web-fragment.xml. * * @param servletName the servlet name. * @param urlPattern the URL pattern. * @author Manfred Riem (mriem@manorrock.com) */ public record WebXmlServletMapping(String servletName, String urlPattern) { } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlServletMultipartConfig.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; /** * A servlet multipart-config inside of web.xml/web-fragment.xml. * * @author Manfred Riem (mriem@manorrock.com) * @author Arjan Tijms */ public class WebXmlServletMultipartConfig { /** * Stores the location to store the files. */ private String location; /** * Stores the maximum size for files. If value is set to -1 it means * unlimited. */ private long maxFileSize = -1; /** * Stores the maximum size for requests. If value is set to -1 it means * unlimited. */ private long maxRequestSize = -1; /** * Stores the threshold for bytes kept in memory before written to disk. */ private int fileSizeThreshold = 0; /** * Default constructor. */ public WebXmlServletMultipartConfig() { } /** * Get the location. * * @return the location. */ public String getLocation() { return location; } /** * Get the max file size. * * @return the max file size. */ public long getMaxFileSize() { return maxFileSize; } /** * Get the max request size. * * @return the max request size. */ public long getMaxRequestSize() { return maxRequestSize; } /** * Get the file size threshold. * * @return the file size threshold. */ public int getFileSizeThreshold() { return fileSizeThreshold; } /** * Set the location. * * @param location the location. */ public void setLocation(String location) { this.location = location; } /** * Set the max file size. * * @param maxFileSize the max file size. */ public void setMaxFileSize(long maxFileSize) { this.maxFileSize = maxFileSize; } /** * Set the max request size. * * @param maxRequestSize the max request size. */ public void setMaxRequestSize(long maxRequestSize) { this.maxRequestSize = maxRequestSize; } /** * Set the file size threshold. * * @param fileSizeThreshold the file size threshold. */ public void setFileSizeThreshold(int fileSizeThreshold) { this.fileSizeThreshold = fileSizeThreshold; } } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlServletSecurityRoleRef.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; /** * A servlet security-role-ref inside of web.xml/web-fragment.xml. * * @param roleName The role name scoped to a specific servlet, and the role name * used in the isUser/CallerInRole methods. * @param roleLink The "global" application role, as defined by a * declareRoles or a security-role * @author Arjan Tijms */ public record WebXmlServletSecurityRoleRef(String roleName, String roleLink) { } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlSessionConfig.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; /** * A session-config inside of web.xml or web-fragment.xml. * * @author Manfred Riem (mriem@manorrock.com) */ public class WebXmlSessionConfig { /** * Stores the cookie config. */ private WebXmlSessionConfigCookieConfig cookieConfig; /** * Stores the timeout. */ private int sessionTimeout; /** * Default constructor. */ public WebXmlSessionConfig() { } /** * Get the cookie config. * * @return the cookie config. */ public WebXmlSessionConfigCookieConfig getCookieConfig() { return cookieConfig; } /** * Get the session timeout. * * @return the session timeout. */ public int getSessionTimeout() { return sessionTimeout; } /** * Set the cookie config. * * @param cookieConfig the cookie config. */ public void setCookieConfig(WebXmlSessionConfigCookieConfig cookieConfig) { this.cookieConfig = cookieConfig; } /** * Set the session timeout. * * @param sessionTimeout the session timeout. */ public void setSessionTimeout(int sessionTimeout) { this.sessionTimeout = sessionTimeout; } } ================================================ FILE: extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/WebXmlSessionConfigCookieConfig.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; /** * A session-config cookie-config inside of web.xml/web-fragment.xml. * * @param name the name. * @author Manfred Riem (mriem@manorrock.com) */ public record WebXmlSessionConfigCookieConfig(String name) { } ================================================ FILE: extension/webxml/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the web.xml processing extension. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.webxml { exports cloud.piranha.extension.webxml; opens cloud.piranha.extension.webxml; requires transitive cloud.piranha.core.api; requires static cloud.piranha.core.impl; requires transitive jakarta.servlet; requires static java.naming; requires static java.sql; requires java.xml; } ================================================ FILE: extension/webxml/src/test/java/cloud/piranha/extension/webxml/TestFilter.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml; import java.io.IOException; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; /** * A test filter * * @author Manfred Riem (mriem@manorrock.com) */ public class TestFilter implements Filter { /** * Filter processing. * * @param request the request. * @param response the response. * @param chain the chain. * @throws IOException when an I/O error occurs. * @throws ServletException when a Servlet error occurs. */ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { } } ================================================ FILE: extension/webxml/src/test/java/cloud/piranha/extension/webxml/TestServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml; import jakarta.servlet.http.HttpServlet; /** * A test servlet * * @author Manfred Riem (mriem@manorrock.com) */ public class TestServlet extends HttpServlet { } ================================================ FILE: extension/webxml/src/test/java/cloud/piranha/extension/webxml/WebXmlExtensionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.core.impl.DefaultWebApplicationExtensionContext; import cloud.piranha.resource.impl.DirectoryResource; import java.io.File; import jakarta.servlet.ServletRegistration; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.Test; /** * The JUnit tests for the WebXmlExtension class. * * @author Manfred Riem (mriem@manorrock.com) */ class WebXmlExtensionTest { /** * Test onStartup method. * * @throws Exception when a serious error occurs. */ @Test void testOnStartup() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/init"))); DefaultWebApplicationExtensionContext context = new DefaultWebApplicationExtensionContext(); context.add(WebXmlExtension.class); context.configure(webApplication); webApplication.initialize(); ServletRegistration registration = webApplication.getServletRegistration("Test Servlet"); assertNotNull(registration); assertFalse(registration.getMappings().isEmpty()); assertEquals("*.html", registration.getMappings().iterator().next()); assertEquals("myvalue", webApplication.getInitParameter("myname")); assertEquals("myservletcontext", webApplication.getServletContextName()); } } ================================================ FILE: extension/webxml/src/test/java/cloud/piranha/extension/webxml/WebXmlInitializerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.core.impl.DefaultWebApplicationClassLoader; import cloud.piranha.resource.impl.DirectoryResource; import java.io.File; import jakarta.servlet.ServletRegistration; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the WebXmlInitializer class. * * @author Manfred Riem (mriem@manorrock.com) */ class WebXmlInitializerTest { /** * Test onStartup method. * * @throws Exception when a serious error occurs. */ @Test void testOnStartup() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/init"))); webApplication.addInitializer(new WebXmlInitializer()); webApplication.initialize(); ServletRegistration registration = webApplication.getServletRegistration("Test Servlet"); assertNotNull(registration); assertFalse(registration.getMappings().isEmpty()); assertEquals("*.html", registration.getMappings().iterator().next()); assertEquals("myvalue", webApplication.getInitParameter("myname")); assertEquals("myservletcontext", webApplication.getServletContextName()); } /** * Test onStartup method. * * @throws Exception when a serious error occurs. */ @Test void testOnStartup2() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/init2"))); webApplication.addInitializer(new WebXmlInitializer()); webApplication.initialize(); assertTrue(webApplication.isInitialized()); } /** * Test onStartup method. * * @throws Exception when a serious error occurs. */ @Test void testOnStartup3() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setClassLoader(new DefaultWebApplicationClassLoader(new File("src/test/webxml/init3"))); webApplication.addInitializer(new WebXmlInitializer()); webApplication.initialize(); assertEquals("/webfragmentInClassesMetaInf", webApplication.getContextPath()); } } ================================================ FILE: extension/webxml/src/test/java/cloud/piranha/extension/webxml/internal/ContextParamTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.extension.webxml.WebXmlInitializer; import cloud.piranha.resource.impl.DirectoryResource; import java.io.File; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; /** * The JUnit tests testing web.xml <context-param>. * * @author Manfred Riem (mriem@manorrock.com) */ class ContextParamTest { /** * Test getInitParameter method. * * @throws Exception when a serious error occurs. */ @Test void testGetInitParameter() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/contextParam"))); webApplication.addInitializer(new WebXmlInitializer()); webApplication.initialize(); assertEquals("myvalue", webApplication.getInitParameter("myname")); } } ================================================ FILE: extension/webxml/src/test/java/cloud/piranha/extension/webxml/internal/DataSourceTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.core.impl.DefaultWebApplicationExtensionContext; import cloud.piranha.extension.herring.HerringExtension; import cloud.piranha.extension.webxml.WebXmlInitializer; import cloud.piranha.resource.impl.DirectoryResource; import java.io.File; import javax.naming.InitialContext; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.Test; /** * The JUnit tests testing web.xml <data-source>. * * @author Manfred Riem (mriem@manorrock.com) */ class DataSourceTest { /** * Test data-source. * * @throws Exception when a serious error occurs. */ @Test void testDataSource() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); DefaultWebApplicationExtensionContext extensionContext = new DefaultWebApplicationExtensionContext(); extensionContext.add(HerringExtension.class); extensionContext.configure(webApplication); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/dataSource"))); webApplication.addInitializer(new WebXmlInitializer()); webApplication.initialize(); InitialContext context = new InitialContext(); assertNotNull(context.lookup("jdbc/demo")); } } ================================================ FILE: extension/webxml/src/test/java/cloud/piranha/extension/webxml/internal/DefaultContextPathTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.extension.webxml.WebXmlInitializer; import cloud.piranha.resource.impl.DirectoryResource; import java.io.File; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; /** * The JUnit tests testing web.xml <default-context-path>. * * @author Manfred Riem (mriem@manorrock.com) */ class DefaultContextPathTest { /** * Test getContextPath method. * * @throws Exception when a serious error occurs. */ @Test void testGetContextPath() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/defaultContextPath"))); webApplication.addInitializer(new WebXmlInitializer()); webApplication.initialize(); assertEquals("/defaultContextPath", webApplication.getContextPath()); } } ================================================ FILE: extension/webxml/src/test/java/cloud/piranha/extension/webxml/internal/DenyUncoveredMethodsTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; /** * The JUnit tests testing web.xml <context-param>. * * @author Manfred Riem (mriem@manorrock.com) */ class DenyUncoveredMethodsTest { // TODO - move this test to security-servlet extension. /** * Test getDenyUncoveredMethods method. * * @throws Exception when a serious error occurs. */ /* @Test void testGetDenyUncoveredMethods() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/denyUncoveredMethods"))); webApplication.addInitializer(new ServletSecurityManagerInitializer()); webApplication.addInitializer(new WebXmlInitializer()); webApplication.initialize(); assertTrue(webApplication.getManager().getSecurityManager().getDenyUncoveredHttpMethods()); } */ } ================================================ FILE: extension/webxml/src/test/java/cloud/piranha/extension/webxml/internal/EffectiveMajorVersionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.extension.webxml.WebXmlInitializer; import cloud.piranha.resource.impl.DirectoryResource; import java.io.File; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; /** * The JUnit tests testing the effective major version. * * @author Manfred Riem (mriem@manorrock.com) */ class EffectiveMajorVersionTest { /** * Test the effective major version. * * @throws Exception when a serious error occurs. */ @Test void testEffectiveMajorVersion() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/effectiveMajorVersion1"))); webApplication.addInitializer(new WebXmlInitializer()); webApplication.initialize(); assertEquals(5, webApplication.getEffectiveMajorVersion()); } } ================================================ FILE: extension/webxml/src/test/java/cloud/piranha/extension/webxml/internal/ErrorPageTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.core.impl.DefaultWebApplicationRequest; import cloud.piranha.core.impl.DefaultWebApplicationResponse; import cloud.piranha.extension.webxml.WebXmlInitializer; import cloud.piranha.resource.impl.DirectoryResource; import static jakarta.servlet.DispatcherType.REQUEST; import java.io.File; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; /** * The JUnit tests testing web.xml <error-page>. * * @author Manfred Riem (mriem@manorrock.com) */ class ErrorPageTest { /** * Test error-page element. * * @throws Exception when a serious error occurs. */ @Test void testErrorPage() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/errorPage"))); webApplication.addInitializer(new WebXmlInitializer()); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setDispatcherType(REQUEST); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setStatus(404); assertEquals("/notfound.jsp", webApplication.getManager() .getErrorPageManager().getErrorPage(null, response)); } /** * Test error-page element. * * @throws Exception when a serious error occurs. */ @Test void testErrorPage2() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/errorPage"))); webApplication.addInitializer(new WebXmlInitializer()); webApplication.initialize(); webApplication.start(); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setDispatcherType(REQUEST); request.setWebApplication(webApplication); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); webApplication.linkRequestAndResponse(request, response); response.setStatus(500); assertEquals("/error.jsp", webApplication.getManager() .getErrorPageManager().getErrorPage(new IllegalStateException(), response)); } } ================================================ FILE: extension/webxml/src/test/java/cloud/piranha/extension/webxml/internal/FilterMappingTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import cloud.piranha.core.api.FilterEnvironment; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.extension.webxml.WebXmlInitializer; import cloud.piranha.resource.impl.DirectoryResource; import jakarta.servlet.FilterRegistration; import java.io.File; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests testing web.xml filter-mapping elements. * * @author Manfred Riem (mriem@manorrock.com) */ class FilterMappingTest { /** * Test filter-mapping. * * @throws Exception when a serious error occurs. */ @Test void testFilterMapping() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/filterMappings"))); webApplication.addInitializer(new WebXmlInitializer()); webApplication.initialize(); assertNotNull(webApplication.getFilterRegistration("TestFilter")); FilterRegistration filter = webApplication.getFilterRegistration("TestFilter"); FilterEnvironment filterEnvironment = (FilterEnvironment) filter; assertTrue(filterEnvironment.isAsyncSupported()); } } ================================================ FILE: extension/webxml/src/test/java/cloud/piranha/extension/webxml/internal/JspConfigTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.resource.impl.DirectoryResource; import cloud.piranha.extension.wasp.WaspJspManagerInitializer; import cloud.piranha.extension.webxml.WebXmlInitializer; import jakarta.servlet.descriptor.JspConfigDescriptor; import java.io.File; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.Test; /** * The JUnit tests testing web.xml <jsp-config>. * * @author Manfred Riem (mriem@manorrock.com) */ class JspConfigTest { /** * Test getJspConfig method. * * @throws Exception when a serious error occurs. */ @Test void tetGetJspConfig() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/jspConfig"))); webApplication.addInitializer(new WaspJspManagerInitializer()); webApplication.addInitializer(new WebXmlInitializer()); webApplication.initialize(); assertNotNull(webApplication.getJspConfigDescriptor()); JspConfigDescriptor descriptor = webApplication.getJspConfigDescriptor(); assertFalse(descriptor.getTaglibs().isEmpty()); } } ================================================ FILE: extension/webxml/src/test/java/cloud/piranha/extension/webxml/internal/MetadataCompleteTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.extension.webxml.WebXmlInitializer; import cloud.piranha.resource.impl.DirectoryResource; import java.io.File; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests testing web.xml metadata-complete attribute. * * @author Manfred Riem (mriem@manorrock.com) */ class MetadataCompleteTest { /** * Test metadata-complete. * * @throws Exception when a serious error occurs. */ @Test void testGetInitParameter() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/metadataComplete"))); webApplication.addInitializer(new WebXmlInitializer()); webApplication.initialize(); assertTrue(webApplication.isMetadataComplete()); } } ================================================ FILE: extension/webxml/src/test/java/cloud/piranha/extension/webxml/internal/MimeMappingTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.extension.webxml.WebXmlInitializer; import cloud.piranha.resource.impl.DirectoryResource; import java.io.File; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; /** * The JUnit tests testing web.xml <mime-mapping>. * * @author Manfred Riem (mriem@manorrock.com) */ class MimeMappingTest { /** * Test getMimeType method. * * @throws Exception when a serious error occurs. */ @Test void testGetMimeType() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/mimeMapping"))); webApplication.addInitializer(new WebXmlInitializer()); webApplication.initialize(); assertEquals("application/x-java-class", webApplication.getMimeType("my.class")); } } ================================================ FILE: extension/webxml/src/test/java/cloud/piranha/extension/webxml/internal/TestWithoutLeadingSlashServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * A simple servlet asserting the servlet-mapping without a leading slash works. * * @author Manfred Riem (mriem@manorrock.com) */ public class TestWithoutLeadingSlashServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { PrintWriter writer = response.getWriter(); writer.println("Working without a leading slash"); } } ================================================ FILE: extension/webxml/src/test/java/cloud/piranha/extension/webxml/internal/WebXmlManagerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import static cloud.piranha.extension.webxml.internal.WebXml.OTHERS_TAG; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; class WebXmlManagerTest { private static WebXml toWebFragment(String fragmentName) { WebXml fragment = new WebXml(); fragment.setFragmentName(fragmentName); return fragment; } @Test void testNoOrdering() { WebXmlManager manager = new WebXmlManager(); WebXml webXml = new WebXml(); manager.setWebXml(webXml); List fragmentNames = Arrays.asList("a", "b", "c", "d", "e"); Collections.shuffle(fragmentNames); manager.setWebFragments(fragmentNames.stream().map(WebXmlManagerTest::toWebFragment).toList()); List orderedFragments = manager.getOrderedFragments(); assertEquals(5, orderedFragments.size()); assertAll(() -> assertTrue(orderedFragments.stream().map(WebXml::getFragmentName).anyMatch("a"::equals)), () -> assertTrue(orderedFragments.stream().map(WebXml::getFragmentName).anyMatch("b"::equals)), () -> assertTrue(orderedFragments.stream().map(WebXml::getFragmentName).anyMatch("c"::equals)), () -> assertTrue(orderedFragments.stream().map(WebXml::getFragmentName).anyMatch("d"::equals)), () -> assertTrue(orderedFragments.stream().map(WebXml::getFragmentName).anyMatch("e"::equals))); } @Test void testAbsoluteOrdering() { WebXmlManager manager = new WebXmlManager(); WebXml webXml = new WebXml(); manager.setWebXml(webXml); webXml.setAbsoluteOrdering(List.of("a", "c", "e")); List fragmentNames = Arrays.asList("a", "b", "c", "d", "e"); Collections.shuffle(fragmentNames); manager.setWebFragments(fragmentNames.stream().map(WebXmlManagerTest::toWebFragment).toList()); List orderedFragments = manager.getOrderedFragments(); assertEquals(3, orderedFragments.size()); assertAll(() -> assertEquals("a", orderedFragments.get(0).getFragmentName()), () -> assertEquals("c", orderedFragments.get(1).getFragmentName()), () -> assertEquals("e", orderedFragments.get(2).getFragmentName())); } @Test void testAbsoluteOrdering2() { WebXmlManager manager = new WebXmlManager(); WebXml webXml = new WebXml(); manager.setWebXml(webXml); webXml.setAbsoluteOrdering(List.of("a", "c", "a", "e")); List fragmentNames = Arrays.asList("a", "b", "c", "d", "e"); Collections.shuffle(fragmentNames); manager.setWebFragments(fragmentNames.stream().map(WebXmlManagerTest::toWebFragment).toList()); List orderedFragments = manager.getOrderedFragments(); assertEquals(3, orderedFragments.size()); assertAll(() -> assertEquals("a", orderedFragments.get(0).getFragmentName()), () -> assertEquals("c", orderedFragments.get(1).getFragmentName()), () -> assertEquals("e", orderedFragments.get(2).getFragmentName())); } @Test void testAbsoluteOrdering3() { WebXmlManager manager = new WebXmlManager(); WebXml webXml = new WebXml(); manager.setWebXml(webXml); webXml.setAbsoluteOrdering(List.of("a", OTHERS_TAG, "c")); List fragmentNames = Arrays.asList("a", "b", "c", "d", "e"); Collections.shuffle(fragmentNames); manager.setWebFragments(fragmentNames.stream().map(WebXmlManagerTest::toWebFragment).toList()); List orderedFragments = manager.getOrderedFragments(); assertEquals(5, orderedFragments.size()); assertAll(() -> assertEquals("a", orderedFragments.get(0).getFragmentName()), () -> assertTrue(orderedFragments.stream().map(WebXml::getFragmentName).anyMatch("c"::equals)), () -> assertTrue(orderedFragments.stream().map(WebXml::getFragmentName).anyMatch("d"::equals)), () -> assertTrue(orderedFragments.stream().map(WebXml::getFragmentName).anyMatch("e"::equals)), () -> assertEquals("c", orderedFragments.get(4).getFragmentName())); } @Test void testAbsoluteOrdering4() { WebXmlManager manager = new WebXmlManager(); WebXml webXml = new WebXml(); manager.setWebXml(webXml); webXml.setAbsoluteOrdering(List.of(OTHERS_TAG, "a", "c", "d")); List fragmentNames = Arrays.asList("a", "b", "c", "d", "e"); Collections.shuffle(fragmentNames); manager.setWebFragments(fragmentNames.stream().map(WebXmlManagerTest::toWebFragment).toList()); List orderedFragments = manager.getOrderedFragments(); assertEquals(5, orderedFragments.size()); assertAll(() -> assertTrue(orderedFragments.stream().map(WebXml::getFragmentName).anyMatch("b"::equals)), () -> assertTrue(orderedFragments.stream().map(WebXml::getFragmentName).anyMatch("e"::equals)), () -> assertEquals("a", orderedFragments.get(2).getFragmentName()), () -> assertEquals("c", orderedFragments.get(3).getFragmentName()), () -> assertEquals("d", orderedFragments.get(4).getFragmentName())); } @Test void testAbsoluteOrdering5() { WebXmlManager manager = new WebXmlManager(); WebXml webXml = new WebXml(); manager.setWebXml(webXml); webXml.setAbsoluteOrdering(List.of("a", "c", "d", OTHERS_TAG)); List fragmentNames = Arrays.asList("a", "b", "c", "d", "e"); Collections.shuffle(fragmentNames); manager.setWebFragments(fragmentNames.stream().map(WebXmlManagerTest::toWebFragment).toList()); List orderedFragments = manager.getOrderedFragments(); assertEquals(5, orderedFragments.size()); assertAll(() -> assertEquals("a", orderedFragments.get(0).getFragmentName()), () -> assertEquals("c", orderedFragments.get(1).getFragmentName()), () -> assertEquals("d", orderedFragments.get(2).getFragmentName()), () -> assertTrue(orderedFragments.stream().map(WebXml::getFragmentName).anyMatch("b"::equals)), () -> assertTrue(orderedFragments.stream().map(WebXml::getFragmentName).anyMatch("e"::equals))); } @Test void testRelativeOrdering() { WebXmlManager manager = new WebXmlManager(); WebXml webXml = new WebXml(); manager.setWebXml(webXml); WebXml fragmentA = toWebFragment("a"); fragmentA.setRelativeOrdering(new WebXml.RelativeOrder(null, List.of(OTHERS_TAG, "c"))); WebXml fragmentB = toWebFragment("b"); fragmentB.setRelativeOrdering(new WebXml.RelativeOrder(List.of(OTHERS_TAG), null)); WebXml fragmentC = toWebFragment("c"); fragmentC.setRelativeOrdering(new WebXml.RelativeOrder(null, List.of(OTHERS_TAG))); WebXml fragmentD = toWebFragment("d"); WebXml fragmentE = toWebFragment("e"); WebXml fragmentF = toWebFragment("f"); fragmentF.setRelativeOrdering(new WebXml.RelativeOrder(List.of(OTHERS_TAG, "b"), null)); manager.setWebFragments(Arrays.asList(fragmentA, fragmentB, fragmentC, fragmentD, fragmentE, fragmentF)); List orderedFragments = manager.getOrderedFragments(); assertEquals(6, orderedFragments.size()); assertAll(() -> assertEquals("f", orderedFragments.get(0).getFragmentName()), () -> assertEquals("b", orderedFragments.get(1).getFragmentName()), () -> assertEquals("d", orderedFragments.get(2).getFragmentName()), () -> assertEquals("e", orderedFragments.get(3).getFragmentName()), () -> assertEquals("c", orderedFragments.get(4).getFragmentName()), () -> assertEquals("a", orderedFragments.get(5).getFragmentName())); } @Test void testRelativeOrdering2() { WebXmlManager manager = new WebXmlManager(); WebXml webXml = new WebXml(); manager.setWebXml(webXml); WebXml fragmentNoID = new WebXml(); fragmentNoID.setRelativeOrdering(new WebXml.RelativeOrder(List.of("c"), List.of(OTHERS_TAG))); WebXml fragmentB = toWebFragment("b"); fragmentB.setRelativeOrdering(new WebXml.RelativeOrder(List.of(OTHERS_TAG), null)); WebXml fragmentC = toWebFragment("c"); WebXml fragmentD = toWebFragment("d"); fragmentD.setRelativeOrdering(new WebXml.RelativeOrder(null, List.of(OTHERS_TAG))); WebXml fragmentE = toWebFragment("e"); fragmentE.setRelativeOrdering(new WebXml.RelativeOrder(List.of(OTHERS_TAG), null)); WebXml fragmentF = toWebFragment("f"); manager.setWebFragments(Arrays.asList(fragmentNoID, fragmentB, fragmentC, fragmentD, fragmentE, fragmentF)); List orderedFragments = manager.getOrderedFragments(); assertEquals(6, orderedFragments.size()); List validOrderings = List.of( "befnullcd", "befnulldc", "ebfnullcd", "ebfnulldc", "ebfdnullc", "ebfdnulld" // the spec example repeats the d fragment ); assertTrue(validOrderings.contains(orderedFragments.stream().map(WebXml::getFragmentName).collect(Collectors.joining("")))); } @Test void testRelativeOrdering3() { WebXmlManager manager = new WebXmlManager(); WebXml webXml = new WebXml(); manager.setWebXml(webXml); WebXml fragmentA = toWebFragment("a"); fragmentA.setRelativeOrdering(new WebXml.RelativeOrder(null, List.of("b"))); WebXml fragmentB = toWebFragment("b"); WebXml fragmentC = toWebFragment("c"); fragmentC.setRelativeOrdering(new WebXml.RelativeOrder(List.of(OTHERS_TAG), null)); WebXml fragmentD = toWebFragment("d"); manager.setWebFragments(Arrays.asList(fragmentA, fragmentB, fragmentC, fragmentD)); List orderedFragments = manager.getOrderedFragments(); assertEquals(4, orderedFragments.size()); List validOrderings = List.of( "cbda", "cdba", "cbad" ); assertTrue(validOrderings.contains(orderedFragments.stream().map(WebXml::getFragmentName).collect(Collectors.joining("")))); } } ================================================ FILE: extension/webxml/src/test/java/cloud/piranha/extension/webxml/internal/WebXmlParserTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.resource.impl.DirectoryResource; import java.io.File; import java.io.InputStream; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the WebXmlParser class. * * @author Manfred Riem (mriem@manorrock.com) */ class WebXmlParserTest { /** * Test parse method. * * @throws Exception when a serious error occurs. */ @Test void testParseWebXml() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/parse"))); InputStream inputStream = webApplication.getResourceAsStream("WEB-INF/web.xml"); WebXmlParser parser = new WebXmlParser(); WebXml webXml = parser.parse(inputStream); assertFalse(webXml.getServlets().isEmpty()); assertEquals(2, webXml.getServlets().size()); assertNotEquals(webXml.getServlets().get(0).getServletName(), webXml.getServlets().get(1).getServletName()); assertTrue(webXml.getServlets().get(0).isAsyncSupported()); assertFalse(webXml.getServlets().get(1).isAsyncSupported()); assertFalse(webXml.getFilters().isEmpty()); assertEquals(1, webXml.getFilters().size()); assertTrue(webXml.getDenyUncoveredHttpMethods()); assertEquals("myServletContextName", webXml.getDisplayName()); assertTrue(webXml.isDistributable()); assertEquals("UTF-8", webXml.getResponseCharacterEncoding()); } /** * Test parse method. * * @throws Exception when a serious error occurs. */ @Test void testParseWebXml2() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/parse2"))); InputStream inputStream = webApplication.getResourceAsStream("WEB-INF/web.xml"); WebXmlParser parser = new WebXmlParser(); WebXml webXml = parser.parse(inputStream); assertFalse(webXml.getDenyUncoveredHttpMethods()); assertNotEquals("myServletContextName", webXml.getDisplayName()); assertFalse(webXml.isDistributable()); assertNotEquals("UTF-8", webXml.getResponseCharacterEncoding()); } /** * Test parse method. * * @throws Exception when a serious error occurs. */ @Test void testParseWebXml3() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/parse3"))); InputStream inputStream = webApplication.getResourceAsStream("WEB-INF/web.xml"); WebXmlParser parser = new WebXmlParser(); WebXml webXml = parser.parse(inputStream); assertEquals(2, webXml.getWelcomeFiles().size()); assertEquals("index.html", webXml.getWelcomeFiles().get(0)); assertEquals("default.jsp", webXml.getWelcomeFiles().get(1)); } /** * Test parse method. * * @throws Exception when a serious error occurs. */ @Test void testParseWebXml4() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/parse4"))); InputStream inputStream = webApplication.getResourceAsStream("WEB-INF/web.xml"); WebXmlParser parser = new WebXmlParser(); WebXml webXml = parser.parse(inputStream); assertEquals(1, webXml.getServlets().size()); String servletName = webXml.getServlets().get(0).getServletName(); assertEquals("Test Servlet", servletName); List servletMappings = webXml.getServletMappings(); assertEquals(2, servletMappings.size()); assertEquals(servletName, servletMappings.get(0).servletName()); assertEquals("/foo", servletMappings.get(0).urlPattern()); assertEquals(servletName, servletMappings.get(1).servletName()); assertEquals("/bar", servletMappings.get(1).urlPattern()); } } ================================================ FILE: extension/webxml/src/test/java/cloud/piranha/extension/webxml/internal/WebXmlProcessorTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import cloud.piranha.core.api.SecurityManager; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.impl.DefaultWebApplication; import static org.junit.jupiter.api.Assertions.assertFalse; import org.junit.jupiter.api.Test; /** * The JUnit tests for the WebXmlProcessor class. * * @author Manfred Riem (mriem@manorrock.com) */ public class WebXmlProcessorTest { /** * Test process method. */ @Test public void testProcessWithSecurityConstraints() { WebXml webXml = new WebXml(); webXml.getSecurityConstraints().add(new WebXmlSecurityConstraint()); WebApplication webApplication = new DefaultWebApplication(); WebXmlProcessor processor = new WebXmlProcessor(); processor.process(webXml, webApplication); SecurityManager securityManager = webApplication.getManager().getSecurityManager(); assertFalse(securityManager.getSecurityConstraints().isEmpty()); } } ================================================ FILE: extension/webxml/src/test/java/cloud/piranha/extension/webxml/internal/WelcomeFileTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.extension.webxml.WebXmlInitializer; import cloud.piranha.extension.welcomefile.internal.InternalWelcomeFileManager; import cloud.piranha.resource.impl.DirectoryResource; import java.io.File; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests testing web.xml <welcome-file>. * * @author Manfred Riem (mriem@manorrock.com) */ class WelcomeFileTest { /** * Test getWelcomeFileList method. * * @throws Exception when a serious error occurs. */ @Test void testGetWelcomeFileList() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/welcomeFile"))); webApplication.getManager().setWelcomeFileManager(new InternalWelcomeFileManager()); webApplication.addInitializer(new WebXmlInitializer()); webApplication.initialize(); assertTrue(webApplication.getManager().getWelcomeFileManager().getWelcomeFileList().contains("index.xhtml")); } /** * Test getWelcomeFileList method. * * @throws Exception when a serious error occurs. */ @Test void testGetWelcomeFileList2() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/welcomeFile"))); webApplication.getManager().setWelcomeFileManager(new InternalWelcomeFileManager()); webApplication.addInitializer(new WebXmlInitializer()); webApplication.initialize(); assertFalse(webApplication.getManager().getWelcomeFileManager().getWelcomeFileList().contains("index.jsp")); } } ================================================ FILE: extension/webxml/src/test/java/cloud/piranha/extension/webxml/internal/WithoutLeadingSlashTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.webxml.internal; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.core.impl.DefaultWebApplicationRequest; import cloud.piranha.core.impl.DefaultWebApplicationResponse; import cloud.piranha.extension.webxml.WebXmlInitializer; import cloud.piranha.resource.impl.DirectoryResource; import java.io.ByteArrayOutputStream; import java.io.File; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests to assert servlet-mappings without a leading slash work. * * @author Manfred Riem (mriem@manorrock.com) */ class WithoutLeadingSlashTest { /** * Test without leading slash.. * * @throws Exception when a serious error occurs. */ @Test void testWithoutLeadingSlash() throws Exception { DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(new File("src/test/webxml/withoutLeadingSlash1"))); webApplication.addInitializer(new WebXmlInitializer()); webApplication.initialize(); webApplication.start(); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApplication); response.setBodyOnly(true); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); response.getWebApplicationOutputStream().setOutputStream(byteOutput); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApplication); request.setServletPath("/thisIsWithoutALeadingSlash"); webApplication.service(request, response); assertTrue(byteOutput.toString().contains("Working without a leading slash")); } } ================================================ FILE: extension/webxml/src/test/webxml/contextParam/WEB-INF/web.xml ================================================ myname myvalue ================================================ FILE: extension/webxml/src/test/webxml/dataSource/WEB-INF/web.xml ================================================ jdbc/demo org.h2.jdbcx.JdbcDataSource jdbc:h2:mem:webxml;DB_CLOSE_DELAY=-1 sa ================================================ FILE: extension/webxml/src/test/webxml/defaultContextPath/WEB-INF/web.xml ================================================ /defaultContextPath ================================================ FILE: extension/webxml/src/test/webxml/denyUncoveredMethods/WEB-INF/web.xml ================================================ ================================================ FILE: extension/webxml/src/test/webxml/effectiveMajorVersion1/WEB-INF/web.xml ================================================ ================================================ FILE: extension/webxml/src/test/webxml/errorPage/WEB-INF/web.xml ================================================ 404 /notfound.jsp java.lang.IllegalStateException /error.jsp ================================================ FILE: extension/webxml/src/test/webxml/filterMappings/WEB-INF/web.xml ================================================ TestFilter cloud.piranha.extension.webxml.TestFilter true name value TestFilter /* REQUEST ================================================ FILE: extension/webxml/src/test/webxml/init/WEB-INF/web.xml ================================================ myservletcontext myname myvalue Test Servlet cloud.piranha.extension.webxml.TestServlet 1 Test Servlet *.html 30 ================================================ FILE: extension/webxml/src/test/webxml/init2/WEB-INF/web.xml ================================================ SecureServlet /SecureServlet GET POST someRole NONE ================================================ FILE: extension/webxml/src/test/webxml/init3/WEB-INF/classes/META-INF/web-fragment.xml ================================================ /webfragmentInClassesMetaInf ================================================ FILE: extension/webxml/src/test/webxml/jspConfig/WEB-INF/web.xml ================================================ http://tag.lib /WEB-INF/taglib.tld ================================================ FILE: extension/webxml/src/test/webxml/metadataComplete/WEB-INF/web.xml ================================================ ================================================ FILE: extension/webxml/src/test/webxml/mimeMapping/WEB-INF/web.xml ================================================ class application/x-java-class ================================================ FILE: extension/webxml/src/test/webxml/parse/WEB-INF/web.xml ================================================ myname myvalue >Test Filter cloud.piranha.extension.webxml.tests.TestFilter Test Servlet cloud.piranha.extension.webxml.test.TestServlet 1 true Test Servlet 2 cloud.piranha.extension.webxml.tests.TestServlet false Test Servlet *.html 30 class application/x-java-class /defaultContextPath myServletContextName UTF-8 ================================================ FILE: extension/webxml/src/test/webxml/parse2/WEB-INF/web.xml ================================================ ================================================ FILE: extension/webxml/src/test/webxml/parse3/WEB-INF/web.xml ================================================ index.html default.jsp ================================================ FILE: extension/webxml/src/test/webxml/parse4/WEB-INF/web.xml ================================================ Test Servlet cloud.piranha.extension.webxml.tests.TestServlet mydescription myrole myrole Test Servlet /foo /bar myrole mywebresourcename mydescription /* myrole ================================================ FILE: extension/webxml/src/test/webxml/welcomeFile/WEB-INF/web.xml ================================================ index.xhtml ================================================ FILE: extension/webxml/src/test/webxml/withoutLeadingSlash1/WEB-INF/web.xml ================================================ TestServlet cloud.piranha.extension.webxml.internal.TestWithoutLeadingSlashServlet TestServlet thisIsWithoutALeadingSlash ================================================ FILE: extension/welcomefile/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-welcomefile jar Piranha - Extension - Welcome File cloud.piranha.core piranha-core-api ${project.version} compile cloud.piranha.core piranha-core-impl ${project.version} test org.junit.jupiter junit-jupiter-engine test org.junit.platform junit-platform-launcher test org.apache.maven.plugins maven-surefire-plugin @{argLine} --add-opens cloud.piranha.extension.welcomefile/cloud.piranha.extension.welcomefile.internal=ALL-UNNAMED org.jacoco jacoco-maven-plugin default-check check BUNDLE INSTRUCTION COVEREDRATIO 0.80 CLASS MISSEDCOUNT 0 ================================================ FILE: extension/welcomefile/src/main/java/cloud/piranha/extension/welcomefile/WelcomeFileExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.welcomefile; import cloud.piranha.extension.welcomefile.internal.InternalWelcomeFileManager; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import java.lang.System.Logger; import static java.lang.System.Logger.Level.DEBUG; /** * The WebApplicationExtension that sets the WelcomeFileManager. * * @author Manfred Riem (mriem@manorrock.com) */ public class WelcomeFileExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(WelcomeFileExtension.class.getName()); /** * Constructor. */ public WelcomeFileExtension() { } @Override public void configure(WebApplication webApplication) { LOGGER.log(DEBUG, "Setting the StandardWelcomeFileManager"); webApplication.getManager().setWelcomeFileManager(new InternalWelcomeFileManager()); } } ================================================ FILE: extension/welcomefile/src/main/java/cloud/piranha/extension/welcomefile/internal/InternalWelcomeFileManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.welcomefile.internal; import cloud.piranha.core.api.WelcomeFileManager; import java.util.ArrayList; import java.util.List; /** * The WelcomeFileManager. * * @author Manfred Riem (mriem@manorrock.com) */ public class InternalWelcomeFileManager implements WelcomeFileManager { /** * Stores the remove defaults flag. */ private boolean removeDefaults; /** * Stores the welcome file list. */ private final ArrayList welcomeFileList; /** * Constructor. */ public InternalWelcomeFileManager() { removeDefaults = true; welcomeFileList = new ArrayList<>(); welcomeFileList.add("index.jsp"); welcomeFileList.add("index.html"); welcomeFileList.add("index.htm"); } @Override public void addWelcomeFile(String welcomeFile) { /* * Upon the first welcome-file added the default list is deleted. */ if (removeDefaults) { welcomeFileList.clear(); removeDefaults = false; } welcomeFileList.add(welcomeFile); } @Override public List getWelcomeFileList() { return welcomeFileList; } } ================================================ FILE: extension/welcomefile/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the welcome-file extension. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.welcomefile { exports cloud.piranha.extension.welcomefile; opens cloud.piranha.extension.welcomefile; requires transitive cloud.piranha.core.api; } ================================================ FILE: extension/welcomefile/src/test/java/cloud/piranha/extension/welcomefile/WelcomeFileExtensionTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.welcomefile; import cloud.piranha.extension.welcomefile.internal.InternalWelcomeFileManager; import cloud.piranha.core.impl.DefaultWebApplication; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the StandardWelcomeFileExtension class. * * @author Manfred Riem (mriem@manorrock.com) */ class WelcomeFileExtensionTest { /** * Test configure method. */ @Test void testConfigure() { System.out.println("configure"); DefaultWebApplication webApplication = new DefaultWebApplication(); WelcomeFileExtension extension = new WelcomeFileExtension(); extension.configure(webApplication); webApplication.initialize(); assertTrue(webApplication.getManager() .getWelcomeFileManager() instanceof InternalWelcomeFileManager); } } ================================================ FILE: extension/welcomefile/src/test/java/cloud/piranha/extension/welcomefile/internal/InternalWelcomeFileManagerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.welcomefile.internal; import cloud.piranha.core.impl.DefaultServlet; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.core.impl.DefaultWebApplicationRequest; import cloud.piranha.core.impl.DefaultWebApplicationResponse; import cloud.piranha.resource.impl.DirectoryResource; import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; import java.io.File; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * The JUnit tests for the StandardWelcomeFileManager class. * * @author Manfred Riem (mriem@manorrock.com) */ class InternalWelcomeFileManagerTest { /** * Test an index.html file. * * @throws Exception when a serious error occurs. */ @Test void testWelcomeFile1() throws Exception { DefaultWebApplication webApp = new DefaultWebApplication(); webApp.addResource(new DirectoryResource(new File("src/test/webapp/welcomefile1"))); webApp.getManager().setWelcomeFileManager(new InternalWelcomeFileManager()); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApp); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApp); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); response.getWebApplicationOutputStream().setOutputStream(byteOutput); webApp.setDefaultServlet(new DefaultServlet()); webApp.initialize(); webApp.start(); webApp.service(request, response); assertEquals(200, response.getStatus()); assertTrue(byteOutput.toString().contains("index.html")); webApp.stop(); } /** * Test a custom welcome file. * * @throws Exception when a serious error occurs. */ @Test void testWelcomeFile2() throws Exception { DefaultWebApplication webApp = new DefaultWebApplication(); webApp.getManager().setWelcomeFileManager(new InternalWelcomeFileManager()); webApp.getManager().getWelcomeFileManager().addWelcomeFile("custom.html"); webApp.addResource(new DirectoryResource(new File("src/test/webapp/welcomefile2"))); DefaultWebApplicationRequest request = new DefaultWebApplicationRequest(); request.setWebApplication(webApp); DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplication(webApp); ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); response.getWebApplicationOutputStream().setOutputStream(byteOutput); webApp.setDefaultServlet(new DefaultServlet()); webApp.initialize(); webApp.start(); webApp.service(request, response); assertEquals(200, response.getStatus()); assertTrue(byteOutput.toString().contains("custom.html")); webApp.stop(); } } ================================================ FILE: extension/welcomefile/src/test/webapp/welcomefile1/index.html ================================================ index.html ================================================ FILE: extension/welcomefile/src/test/webapp/welcomefile2/custom.html ================================================ custom.html ================================================ FILE: extension/weld/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-weld jar Piranha - Extension - Weld UTF-8 org.jboss.weld.servlet weld-servlet-core compile org.jboss.spec.jakarta.el jboss-el-api_3.0_spec org.jboss.spec.jakarta.annotation jboss-annotations-api_1.3_spec cloud.piranha.core piranha-core-impl ${project.version} provided jakarta.annotation jakarta.annotation-api provided jakarta.authentication jakarta.authentication-api provided jakarta.el jakarta.el-api provided jakarta.enterprise jakarta.enterprise.cdi-api provided jakarta.security.enterprise jakarta.security.enterprise-api provided jakarta.transaction jakarta.transaction-api provided jakarta.enterprise jakarta.enterprise.cdi-el-api runtime ================================================ FILE: extension/weld/src/main/java/cloud/piranha/extension/weld/RealtimeHttpServletRequestWrapper.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.weld; import java.io.BufferedReader; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.Principal; import java.util.Collection; import java.util.Enumeration; import java.util.Locale; import java.util.Map; import jakarta.servlet.AsyncContext; import jakarta.servlet.DispatcherType; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.ServletInputStream; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpUpgradeHandler; import jakarta.servlet.http.Part; /** * An HttpServletRequest wrapper that consistently consults the getWrapped() method for every operation. * * @author Arjan Tijms * @author Manfred Riem (mriem@manorrock.com) */ public abstract class RealtimeHttpServletRequestWrapper implements HttpServletRequest { /** * Constructor. */ public RealtimeHttpServletRequestWrapper() { } /** * {@return the async context} */ @Override public AsyncContext getAsyncContext() { return getWrapped().getAsyncContext(); } /** * Get the attribute. * * @param name the name. * @return the value. */ @Override public Object getAttribute(String name) { return getWrapped().getAttribute(name); } /** * {@return the attribute names} */ @Override public Enumeration getAttributeNames() { return getWrapped().getAttributeNames(); } /** * {@return the character encoding} */ @Override public String getCharacterEncoding() { return getWrapped().getCharacterEncoding(); } /** * {@return the content length} */ @Override public int getContentLength() { return getWrapped().getContentLength(); } /** * {@return the content length} */ @Override public long getContentLengthLong() { return getWrapped().getContentLengthLong(); } /** * {@return the content type} */ @Override public String getContentType() { return getWrapped().getContentType(); } /** * Get the dispatcher type. */ @Override public DispatcherType getDispatcherType() { return getWrapped().getDispatcherType(); } /** * {@return the input stream} * @throws IOException when an I/O error occurs. */ @Override public ServletInputStream getInputStream() throws IOException { return getWrapped().getInputStream(); } /** * {@return the local address} */ @Override public String getLocalAddr() { return getWrapped().getLocalAddr(); } /** * {@return the local name} */ @Override public String getLocalName() { return getWrapped().getLocalName(); } /** * {@return the local port} */ @Override public int getLocalPort() { return getWrapped().getLocalPort(); } /** * {@return the locale} */ @Override public Locale getLocale() { return getWrapped().getLocale(); } /** * {@return the locales} */ @Override public Enumeration getLocales() { return getWrapped().getLocales(); } /** * Get the parameter. * * @param name the name. * @return the value, or null if not found. */ @Override public String getParameter(String name) { return getWrapped().getParameter(name); } /** * {@return the parameter map} */ @Override public Map getParameterMap() { return getWrapped().getParameterMap(); } /** * {@return the parameter names} */ @Override public Enumeration getParameterNames() { return getWrapped().getParameterNames(); } /** * Get the parameter values. * * @param name the name. * @return the values. */ @Override public String[] getParameterValues(String name) { return getWrapped().getParameterValues(name); } /** * {@return the protocol} */ @Override public String getProtocol() { return getWrapped().getProtocol(); } /** * Get the reader. * * @return the reader. * @throws IOException when an I/O error occurs. */ @Override public BufferedReader getReader() throws IOException { return getWrapped().getReader(); } /** * {@return the remote address} */ @Override public String getRemoteAddr() { return getWrapped().getRemoteAddr(); } /** * {@return the remote host} */ @Override public String getRemoteHost() { return getWrapped().getRemoteHost(); } /** * {@return the remote port} */ @Override public int getRemotePort() { return getWrapped().getRemotePort(); } /** * {@return the wrapped request} */ public ServletRequest getRequest() { return getWrapped(); } /** * {@return the request dispatcher} * @param path the path. */ @Override public RequestDispatcher getRequestDispatcher(String path) { return getWrapped().getRequestDispatcher(path); } /** * {@return the scheme} */ @Override public String getScheme() { return getWrapped().getScheme(); } /** * {@return the server name} */ @Override public String getServerName() { return getWrapped().getServerName(); } /** * {@return the server port} */ @Override public int getServerPort() { return getWrapped().getServerPort(); } /** * {@return the servlet context} */ @Override public ServletContext getServletContext() { return getWrapped().getServletContext(); } /** * Is async started. * * @return true if async started, false otherwise. */ @Override public boolean isAsyncStarted() { return getWrapped().isAsyncStarted(); } /** * Is async supported. * * @return true if async is supported, false otherwise. */ @Override public boolean isAsyncSupported() { return getWrapped().isAsyncSupported(); } /** * Is secure. * * @return true if it is, false otherwise. */ @Override public boolean isSecure() { return getWrapped().isSecure(); } /** * Is this a wrapper for the given request. * * @param wrapped the wrapped request. * @return true if it is, false otherwise. */ public boolean isWrapperFor(ServletRequest wrapped) { if (this.getWrapped() == wrapped) { return true; } else if (this.getWrapped() instanceof RealtimeHttpServletRequestWrapper realtimeWrapped) { return realtimeWrapped.isWrapperFor(wrapped); } else { return false; } } /** * Are we a wrapper for the given type. * * @param wrappedType the wrapped type. * @return true if we are, false otherwise. */ public boolean isWrapperFor(Class wrappedType) { if (!ServletRequest.class.isAssignableFrom(wrappedType)) { throw new IllegalArgumentException("Given class " + wrappedType.getName() + " not a subinterface of " + ServletRequest.class.getName()); } if (wrappedType.isAssignableFrom(getWrapped().getClass())) { return true; } else if (getWrapped() instanceof RealtimeHttpServletRequestWrapper realtimeWrapped) { return realtimeWrapped.isWrapperFor(wrappedType); } else { return false; } } /** * Remove the attribute. * * @param name the name. */ @Override public void removeAttribute(String name) { getWrapped().removeAttribute(name); } /** * Set the attribute. * * @param name the name. * @param object the object value. */ @Override public void setAttribute(String name, Object object) { getWrapped().setAttribute(name, object); } /** * Set the character encoding. * * @param characterEncoding the character encoding. * @throws UnsupportedEncodingException when trying to set an unsupported * character encoding. */ @Override public void setCharacterEncoding(String characterEncoding) throws UnsupportedEncodingException { getWrapped().setCharacterEncoding(characterEncoding); } /** * Start async processing. * * @throws IllegalStateException when not allowed. */ @Override public AsyncContext startAsync() throws IllegalStateException { return getWrapped().startAsync(); } /** * Start async processing. * * @param servletRequest the servlet request. * @param servletResponse the servlet response. */ @Override public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { return getWrapped().startAsync(servletRequest, servletResponse); } /** * Authenticate the request. * * @param response the response. * @throws IOException when an I/O error occurs. * @throws ServletException when a Servlet error occurs. */ @Override public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { return getWrapped().authenticate(response); } /** * Change the session id. * * @return the new session id. */ @Override public String changeSessionId() { return getWrapped().changeSessionId(); } /** * {@return the auth type} */ @Override public String getAuthType() { return getWrapped().getAuthType(); } /** * {@return the context path} */ @Override public String getContextPath() { return getWrapped().getContextPath(); } /** * Get the cookies. * * @return the cookies, or null if none. */ @Override public Cookie[] getCookies() { return getWrapped().getCookies(); } /** * Get the date header. * * @param name the name. * @return the date, or -1 if not found. */ @Override public long getDateHeader(String name) { return getWrapped().getDateHeader(name); } /** * Get the header. * * @param name the name. * @return the value, or null if not found. */ @Override public String getHeader(String name) { return getWrapped().getHeader(name); } /** * {@return the header names} */ @Override public Enumeration getHeaderNames() { return getWrapped().getHeaderNames(); } /** * Get the headers. * * @param name the name. * @return the values. */ @Override public Enumeration getHeaders(String name) { return getWrapped().getHeaders(name); } /** * Get the int header. * * @param name the name. * @return the int, or -1 if not found. */ @Override public int getIntHeader(String name) { return getWrapped().getIntHeader(name); } /** * {@return the method} */ @Override public String getMethod() { return getWrapped().getMethod(); } /** * Get the part. * * @param name the name. * @throws IOException when an I/O error occurs. * @throws ServletException when a Servlet error occurs. */ @Override public Part getPart(String name) throws IOException, ServletException { return getWrapped().getPart(name); } /** * Get the parts. * * @return the parts. * @throws IOException when an I/O error occurs. * @throws ServletException when a Servlet error occurs. */ @Override public Collection getParts() throws IOException, ServletException { return getWrapped().getParts(); } /** * {@return the path info} */ @Override public String getPathInfo() { return getWrapped().getPathInfo(); } /** * {@return the path translated} */ @Override public String getPathTranslated() { return getWrapped().getPathTranslated(); } /** * {@return the query string} */ @Override public String getQueryString() { return getWrapped().getQueryString(); } /** * {@return the remote user} */ @Override public String getRemoteUser() { return getWrapped().getRemoteUser(); } /** * {@return the request URI} */ @Override public String getRequestURI() { return getWrapped().getRequestURI(); } /** * {@return the request URL} */ @Override public StringBuffer getRequestURL() { return getWrapped().getRequestURL(); } /** * {@return the requested session id} */ @Override public String getRequestedSessionId() { return getWrapped().getRequestedSessionId(); } /** * {@return the servlet path} */ @Override public String getServletPath() { return getWrapped().getServletPath(); } /** * {@return the HTTP session} */ @Override public HttpSession getSession() { return getWrapped().getSession(); } /** * Get the HTTP session. * * @param create the create flag. * @return the HTTP session, or null if not able to create. */ @Override public HttpSession getSession(boolean create) { return getWrapped().getSession(create); } /** * {@return the user principal} */ @Override public Principal getUserPrincipal() { return getWrapped().getUserPrincipal(); } /** * {@return the wrapped request} */ protected abstract HttpServletRequest getWrapped(); /** * Is the requested session id from a cookie. * * @return true if it is, false otherwise. */ @Override public boolean isRequestedSessionIdFromCookie() { return getWrapped().isRequestedSessionIdFromCookie(); } /** * Is the requested session id from a URL. * * @return true if it is, false otherwise. */ @Override public boolean isRequestedSessionIdFromURL() { return getWrapped().isRequestedSessionIdFromURL(); } /** * Is the requested session id valid. * * @return true if it is, false otherwise. */ @Override public boolean isRequestedSessionIdValid() { return getWrapped().isRequestedSessionIdValid(); } /** * Is the user in the role. * * @param role the role. * @return true if the user is, false otherwise. */ @Override public boolean isUserInRole(String role) { return getWrapped().isUserInRole(role); } /** * Login. * * @param username the username. * @param password the password. * @throws ServletException when a Servlet error occurs. */ @Override public void login(String username, String password) throws ServletException { getWrapped().login(username, password); } /** * Logout. * * @throws ServletException when a Servlet error occurs. */ @Override public void logout() throws ServletException { getWrapped().logout(); } /** * Upgrade the request. * * @param the type of HTTP upgrade handler class. * @param handlerClass the handler class. * @return the HTTP upgrade handler. * @throws IOException when an I/O error occurs. * @throws ServletException when a Servlet error occurs. */ @Override public T upgrade(Class handlerClass) throws IOException, ServletException { return getWrapped().upgrade(handlerClass); } } ================================================ FILE: extension/weld/src/main/java/cloud/piranha/extension/weld/WeldCDI.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.weld; import jakarta.enterprise.inject.spi.BeanManager; import org.jboss.weld.AbstractCDI; import org.jboss.weld.manager.api.WeldManager; /** * The CDI for Weld. * * @author Manfred Riem (mriem@manorrock.com) */ public class WeldCDI extends AbstractCDI { /** * Stores the manager. */ private WeldManager manager; /** * Constructor. * * @param manager the WeldManager. * */ public WeldCDI(WeldManager manager) { this.manager = manager; } /** * {@return the bean manager} */ @Override public BeanManager getBeanManager() { return manager; } /** * Set the WeldManager. * * @param manager the WeldManager. */ public void setWeldManager(WeldManager manager) { this.manager = manager; } } ================================================ FILE: extension/weld/src/main/java/cloud/piranha/extension/weld/WeldContainer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.weld; import jakarta.enterprise.inject.spi.CDI; import javax.naming.InitialContext; import javax.naming.NamingException; import org.jboss.weld.environment.servlet.Container; import org.jboss.weld.environment.servlet.ContainerContext; import org.jboss.weld.manager.api.WeldManager; import org.jboss.weld.resources.spi.ResourceLoader; /** * The Weld container. * * @author Manfred Riem (mriem@manorrock.com) */ public class WeldContainer implements Container { /** * Constructor. */ public WeldContainer() { } /** * Destroy the container. * * @param context the container context. */ @Override public void destroy(ContainerContext context) { // nothing to do here. } /** * Initialize the container. * * @param context the container context. */ @Override public void initialize(ContainerContext context) { try { CDI.setCDIProvider(new WeldProvider()); } catch (IllegalStateException ise) { } try { WeldManager manager = context.getManager(); WeldProvider.setCDI(new WeldCDI(manager)); new InitialContext().rebind("java:comp/BeanManager", manager); } catch (NamingException ne) { throw new RuntimeException(ne); } } /** * Touch the container. * * @param resourceLoader the resource loader. * @param context the container context. * @return true * @throws Exception when a serious error occurs. */ @Override public boolean touch(ResourceLoader resourceLoader, ContainerContext context) throws Exception { return true; } } ================================================ FILE: extension/weld/src/main/java/cloud/piranha/extension/weld/WeldExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.weld; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; /** * The extension that integrates Weld. * * @author Manfred Riem (mriem@manorrock.com) */ public class WeldExtension implements WebApplicationExtension { /** * Constructor. */ public WeldExtension() { } @Override public void configure(WebApplication webApplication) { webApplication.addInitializer(WeldInitializer.class.getName()); } } ================================================ FILE: extension/weld/src/main/java/cloud/piranha/extension/weld/WeldHttpServletRequest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.weld; import static cloud.piranha.core.api.CurrentRequestHolder.CURRENT_REQUEST_ATTRIBUTE; import jakarta.servlet.http.HttpServletRequest; import cloud.piranha.core.impl.DefaultCurrentRequestHolder; import cloud.piranha.core.api.CurrentRequestHolder; import jakarta.servlet.ServletConnection; /** * An HttpServletRequest wrapper that always delegates every operation to what has been set as * the current request. * *

* This allows Weld to hold on to a single HttpServletRequest instance, which can then be set to * point to another HttpServletRequest instance as the current one that the request uses changes. * *

* This instance changes for example after an authentication module or filter has provided a new * HttpServletRequest, or when a dispatch or include is performed. * * @author Arjan Tijms * */ public class WeldHttpServletRequest extends RealtimeHttpServletRequestWrapper { /** * Stores the current request holder. */ private final CurrentRequestHolder currentRequestHolder; /** * Constructor. * * @param request the HTTP servlet request. */ public WeldHttpServletRequest(HttpServletRequest request) { currentRequestHolder = new DefaultCurrentRequestHolder(request); request.setAttribute(CURRENT_REQUEST_ATTRIBUTE, currentRequestHolder); } @Override protected HttpServletRequest getWrapped() { return currentRequestHolder.getRequest(); } /* REVIEW FOR SERVLET 6 */ @Override public String getRequestId() { throw new UnsupportedOperationException("Not supported yet."); } /* REVIEW FOR SERVLET 6 */ @Override public String getProtocolRequestId() { throw new UnsupportedOperationException("Not supported yet."); } /* REVIEW FOR SERVLET 6 */ @Override public ServletConnection getServletConnection() { throw new UnsupportedOperationException("Not supported yet."); } } ================================================ FILE: extension/weld/src/main/java/cloud/piranha/extension/weld/WeldInitListener.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.weld; import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletRequestEvent; import jakarta.servlet.http.HttpServletRequest; import org.jboss.weld.environment.servlet.Listener; import org.jboss.weld.servlet.api.ServletListener; import org.jboss.weld.servlet.api.helpers.ForwardingServletListener; import cloud.piranha.core.api.CurrentRequestHolder; /** * This Piranha specific Weld initializer forwards all initialization * to the original Weld initializer, but modifies the HttpServletRequest * that's passed into it. * *

* The purpose of this is making sure Weld is able to access the current * HttpServletRequest as that changes throughout the request processing * pipeline. * * @see WeldHttpServletRequest * @see CurrentRequestHolder * * @author Arjan Tijms * */ public class WeldInitListener extends ForwardingServletListener { /** * Stores the weld target listener. */ private ServletListener weldTargetListener = new Listener(); /** * Constructor. */ public WeldInitListener() { } @Override public void contextInitialized(ServletContextEvent sce) { // Do nothing } @Override public void requestInitialized(ServletRequestEvent sre) { super.requestInitialized(new ServletRequestEvent( sre.getServletContext(), new WeldHttpServletRequest((HttpServletRequest)sre.getServletRequest()))); } @Override public ServletListener delegate() { return weldTargetListener; } } ================================================ FILE: extension/weld/src/main/java/cloud/piranha/extension/weld/WeldInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.weld; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import java.util.Set; import cloud.piranha.core.api.WebApplication; /** * The Weld Integration ServletContainerInitializer. * * @author Manfred Riem (mriem@manorrock.com) */ public class WeldInitializer implements ServletContainerInitializer { /** * Constructor. */ public WeldInitializer() { } /** * On startup. * * @param classes the annotated classes. * @param servletContext the servlet context. * @throws ServletException when a serious error occurs. */ @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { servletContext.setInitParameter("WELD_CONTEXT_ID_KEY", servletContext.toString()); if (Boolean.valueOf(System.getProperty("piranha.emptyBeansXmlModeAll", "false"))) { servletContext.setInitParameter("org.jboss.weld.environment.servlet.emptyBeansXmlModeAll", "true"); } servletContext.setInitParameter("org.jboss.weld.environment.servlet.archive.isolation", "false"); WebApplication webApplication = (WebApplication) servletContext; WeldInitListener weldInitListener = webApplication.createListener(WeldInitListener.class); try { webApplication.getManager().setObjectInstanceManager(new WeldObjectInstanceManager()); servletContext.addListener(weldInitListener); } catch (IllegalStateException e) { // CDI not available } } } ================================================ FILE: extension/weld/src/main/java/cloud/piranha/extension/weld/WeldObjectInstanceManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.weld; import cloud.piranha.core.api.ObjectInstanceManager; import java.util.EventListener; import jakarta.enterprise.inject.spi.BeanManager; import jakarta.enterprise.inject.spi.CDI; import jakarta.enterprise.inject.spi.Unmanaged; import jakarta.servlet.Filter; import jakarta.servlet.Servlet; import jakarta.servlet.ServletException; import java.lang.System.Logger; import static java.lang.System.Logger.Level.WARNING; /** * The Weld variant of the ObjectInstanceManager. * * @author Manfred Riem (mriem@manorrock.com) */ public class WeldObjectInstanceManager implements ObjectInstanceManager { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(WeldObjectInstanceManager.class.getName()); /** * Constructor. */ public WeldObjectInstanceManager() { } @Override public T createFilter(Class filterClass) throws ServletException { T result = null; boolean constructed = false; try { BeanManager beanManager = CDI.current().getBeanManager(); Unmanaged unmanaged = new Unmanaged<>(beanManager, filterClass); Unmanaged.UnmanagedInstance unmanagedInstance = unmanaged.newInstance(); result = unmanagedInstance.produce().inject().postConstruct().get(); constructed = true; } catch (Exception exception) { LOGGER.log(WARNING, "Unable to create Filter using CDI", exception); } if (!constructed) { try { result = filterClass.getDeclaredConstructor().newInstance(); } catch (Exception exception) { throw new ServletException("Unable to create Listener using new", exception); } } return result; } @Override public T createListener(Class clazz) throws ServletException { T result = null; boolean constructed = false; try { BeanManager beanManager = CDI.current().getBeanManager(); Unmanaged unmanaged = new Unmanaged<>(beanManager, clazz); Unmanaged.UnmanagedInstance unmanagedInstance = unmanaged.newInstance(); result = unmanagedInstance.produce().inject().postConstruct().get(); constructed = true; } catch (Exception exception) { LOGGER.log(WARNING, "Unable to create Listener using CDI", exception); } if (!constructed) { try { result = clazz.getDeclaredConstructor().newInstance(); } catch (Exception exception) { throw new ServletException("Unable to create Listener using new", exception); } } return result; } @Override public T createServlet(Class servletClass) throws ServletException { T result = null; boolean constructed = false; try { BeanManager beanManager = CDI.current().getBeanManager(); Unmanaged unmanaged = new Unmanaged<>(beanManager, servletClass); Unmanaged.UnmanagedInstance unmanagedInstance = unmanaged.newInstance(); result = unmanagedInstance.produce().inject().postConstruct().get(); constructed = true; } catch (Exception exception) { LOGGER.log(WARNING, "Unable to create Servlet using CDI", exception); } if (!constructed) { try { result = servletClass.getDeclaredConstructor().newInstance(); } catch (Exception exception) { throw new ServletException("Unable to create Servlet using new", exception); } } return result; } } ================================================ FILE: extension/weld/src/main/java/cloud/piranha/extension/weld/WeldProvider.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.weld; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import jakarta.enterprise.inject.spi.CDI; import jakarta.enterprise.inject.spi.CDIProvider; /** * The Weld CDI provider. * * @author Manfred Riem (mriem@manorrock.com) */ public class WeldProvider implements CDIProvider { /** * Stores the instances. */ private static final Map> INSTANCES = new ConcurrentHashMap<>(); /** * Constructor. */ public WeldProvider() { } /** * {@return the CDI} */ @Override public CDI getCDI() { return INSTANCES.get(Thread.currentThread().getContextClassLoader()); } /** * Set the CDI. * * @param cdi the CDI. */ public static void setCDI(CDI cdi) { INSTANCES.put(Thread.currentThread().getContextClassLoader(), cdi); } } ================================================ FILE: extension/weld/src/main/java/cloud/piranha/extension/weld/WeldSecurityService.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.weld; import java.security.Principal; import jakarta.enterprise.inject.Instance; import jakarta.enterprise.inject.spi.CDI; import jakarta.security.enterprise.SecurityContext; import org.jboss.weld.security.spi.SecurityServices; /** * The implementation of this Weld SPI provides the current principal for injection by CDI. * *

* Implementation detail: In Weld 3.1.* org.jboss.weld.bean.builtin.ee.PrincipalBean calls this. * * @author Arjan Tijms * */ public class WeldSecurityService implements SecurityServices { /** * Constructor. */ public WeldSecurityService() { } @Override public Principal getPrincipal() { Instance securityServiceInstance = CDI.current().select(SecurityContext.class); if (securityServiceInstance.isResolvable()) { return securityServiceInstance.get().getCallerPrincipal(); } return null; } @Override public void cleanup() { // Nothing to do here } } ================================================ FILE: extension/weld/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ import cloud.piranha.extension.weld.WeldContainer; import cloud.piranha.extension.weld.WeldSecurityService; import org.jboss.weld.bootstrap.api.Service; import org.jboss.weld.environment.servlet.Container; /** * This module delivers the Weld integration extension. * *

* This extension integrates Weld into Piranha. See * https://github.com/weld/core for more information about its project. *

* * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.weld { exports cloud.piranha.extension.weld; opens cloud.piranha.extension.weld; provides Container with WeldContainer; provides Service with WeldSecurityService; requires transitive cloud.piranha.core.api; requires cloud.piranha.core.impl; requires jakarta.inject; requires jakarta.cdi; requires jakarta.security; requires java.naming; requires weld.api; requires weld.core.impl; requires weld.servlet.core; requires transitive weld.spi; } ================================================ FILE: extension/weld/src/main/resources/META-INF/services/org.jboss.weld.bootstrap.api.Service ================================================ cloud.piranha.extension.weld.WeldSecurityService ================================================ FILE: extension/weld/src/main/resources/META-INF/services/org.jboss.weld.environment.servlet.Container ================================================ cloud.piranha.extension.weld.WeldContainer ================================================ FILE: extension/yasson/pom.xml ================================================ 4.0.0 cloud.piranha.extension project 25.4.0-SNAPSHOT piranha-extension-yasson jar Piranha - Extension - Yasson cloud.piranha.core piranha-core-api ${project.version} compile cloud.piranha.extension piranha-extension-scinitializer ${project.version} provided org.eclipse yasson runtime ================================================ FILE: extension/yasson/src/main/java/cloud/piranha/extension/yasson/YassonExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.extension.yasson; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import java.lang.System.Logger; import static java.lang.System.Logger.Level.TRACE; /** * The extension that delivers Eclipse Yasson to Piranha. * * @author Manfred Riem (mriem@manorrock.com) */ public class YassonExtension implements WebApplicationExtension { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(YassonExtension.class.getName()); /** * Constructor. */ public YassonExtension() { } /** * Configure the extension. * * @param webApplication the web application. */ @Override public void configure(WebApplication webApplication) { LOGGER.log(TRACE, "Configuring Yasson extension"); } } ================================================ FILE: extension/yasson/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Eclipse Yasson integration extension. * *

* This extension integrates Eclipse Yasson into Piranha. See * https://github.com/eclipse-ee4j/yasson for more information about its * project. *

* * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.extension.yasson { requires cloud.piranha.core.api; requires static cloud.piranha.extension.scinitializer; } ================================================ FILE: feature/api/pom.xml ================================================ 4.0.0 cloud.piranha.feature project 25.4.0-SNAPSHOT piranha-feature-api jar Piranha - Feature - API ================================================ FILE: feature/api/src/main/java/cloud/piranha/feature/api/Feature.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.api; /** * The Feature API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface Feature { /** * Destroy the feature. */ default void destroy() { } /** * Get the feature manager. * * @return the feature manager. */ FeatureManager getFeatureManager(); /** * Initialize the feature. */ default void init() { } /** * Set the feature manager. * * @param featureManager the feature manager. */ void setFeatureManager(FeatureManager featureManager); /** * Start the feature. */ default void start() { } /** * Stop the feature. */ default void stop() { } } ================================================ FILE: feature/api/src/main/java/cloud/piranha/feature/api/FeatureManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.api; import java.util.List; /** * The Feature Manager API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface FeatureManager { /** * Add a feature. * * @param feature the feature. */ void addFeature(Feature feature); /** * Get the features. * * @return the features. */ List getFeatures(); /** * Stop the features. */ void stop(); } ================================================ FILE: feature/api/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module defines the Feature API. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.feature.api { exports cloud.piranha.feature.api; opens cloud.piranha.feature.api; } ================================================ FILE: feature/crac/pom.xml ================================================ 4.0.0 cloud.piranha.feature project 25.4.0-SNAPSHOT piranha-feature-crac jar Piranha - Feature - CRaC cloud.piranha.feature piranha-feature-impl ${project.version} compile cloud.piranha.feature piranha-feature-http ${project.version} compile true cloud.piranha.feature piranha-feature-https ${project.version} compile true cloud.piranha.http piranha-http-crac ${project.version} compile org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test org.apache.maven.plugins maven-surefire-plugin false ================================================ FILE: feature/crac/src/main/java/cloud/piranha/feature/crac/CracFeature.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.crac; import cloud.piranha.feature.api.Feature; import cloud.piranha.feature.http.HttpFeature; import cloud.piranha.feature.https.HttpsFeature; import cloud.piranha.feature.impl.DefaultFeature; import cloud.piranha.http.api.HttpServer; import java.lang.System.Logger; import static java.lang.System.Logger.Level.ERROR; import java.lang.reflect.InvocationTargetException; /** * The CRaC feature that enables Project CRaC. * * @author Manfred Riem (mriem@manorrock.com) */ public class CracFeature extends DefaultFeature { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(CracFeature.class.getName()); /** * Constructor. */ public CracFeature() { } /** * Create the CRaC HttpServer instance. * * @param httpServer the HttpServer instance to wrap. * @return the CRaC HttpServer instance. */ private HttpServer createCracHttpServer(HttpServer httpServer) { HttpServer cracHttpServer = null; try { cracHttpServer = (HttpServer) Class .forName("cloud.piranha.http.crac.CracHttpServer") .getDeclaredConstructor(HttpServer.class) .newInstance(httpServer); } catch (ClassNotFoundException | IllegalAccessException | IllegalArgumentException | InstantiationException | NoSuchMethodException | SecurityException | InvocationTargetException t) { LOGGER.log(ERROR, "Unable to construct CracHttpServer", t); } return cracHttpServer; } @Override public void init() { for (Feature feature : featureManager.getFeatures()) { if (feature instanceof HttpFeature httpFeature) { HttpServer cracHttpServer = createCracHttpServer(httpFeature.getHttpServer()); httpFeature.setHttpServer(cracHttpServer); } if (feature instanceof HttpsFeature httpsFeature) { HttpServer cracHttpServer = createCracHttpServer(httpsFeature.getHttpsServer()); httpsFeature.setHttpsServer(cracHttpServer); } } } } ================================================ FILE: feature/crac/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the CRaC Feature. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.feature.crac { exports cloud.piranha.feature.crac; opens cloud.piranha.feature.crac; requires cloud.piranha.feature.impl; requires static cloud.piranha.feature.http; requires static cloud.piranha.feature.https; requires cloud.piranha.http.api; requires cloud.piranha.http.crac; } ================================================ FILE: feature/crac/src/test/java/cloud/piranha/feature/crac/CracFeatureTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.crac; import cloud.piranha.feature.http.HttpFeature; import cloud.piranha.feature.https.HttpsFeature; import cloud.piranha.feature.impl.DefaultFeatureManager; import cloud.piranha.http.api.HttpServer; import static org.junit.jupiter.api.Assertions.assertNotEquals; import org.junit.jupiter.api.Test; /** * The JUnit tests for the CracFeature class. * * @author Manfred Riem (mriem@manorrock.com) */ class CracFeatureTest { /** * Test init method. */ @Test void testInit() { DefaultFeatureManager manager = new DefaultFeatureManager(); HttpFeature httpFeature = new HttpFeature(); httpFeature.setFeatureManager(manager); manager.addFeature(httpFeature); httpFeature.init(); HttpServer server = httpFeature.getHttpServer(); CracFeature cracFeature = new CracFeature(); cracFeature.setFeatureManager(manager); manager.addFeature(cracFeature); cracFeature.init(); assertNotEquals(server, httpFeature.getHttpServer()); } /** * Test init method. */ @Test void testInit2() { DefaultFeatureManager manager = new DefaultFeatureManager(); HttpsFeature httpsFeature = new HttpsFeature(); httpsFeature.setFeatureManager(manager); manager.addFeature(httpsFeature); httpsFeature.init(); HttpServer server = httpsFeature.getHttpsServer(); CracFeature cracFeature = new CracFeature(); cracFeature.setFeatureManager(manager); manager.addFeature(cracFeature); cracFeature.init(); assertNotEquals(server, httpsFeature.getHttpsServer()); } } ================================================ FILE: feature/exitonstop/pom.xml ================================================ 4.0.0 cloud.piranha.feature project 25.4.0-SNAPSHOT piranha-feature-exitonstop jar Piranha - Feature - Exit on Stop cloud.piranha.feature piranha-feature-impl ${project.version} compile org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test ================================================ FILE: feature/exitonstop/src/main/java/cloud/piranha/feature/exitonstop/ExitOnStopFeature.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.exitonstop; import cloud.piranha.feature.impl.DefaultFeature; /** * The Exit on Stop feature. * *

* The Exit on Stop feature will exit the JVM after all the features have * asked to be stopped. It waits for a predefined amount of time before it * calls System.exit. *

* * @author Manfred Riem (mriem@manorrock.com) */ public class ExitOnStopFeature extends DefaultFeature { /** * Stores the exiting flag. */ private volatile boolean exiting; /** * Constructor. */ public ExitOnStopFeature() { exiting = false; } /** * Are we in the process of exiting? * * @return true if we are, false otherwise. */ public boolean isExiting() { return exiting; } @Override public void stop() { Thread thread = new Thread() { @Override public void run() { exiting = true; try { Thread.sleep(2000); } catch(InterruptedException ie) { Thread.currentThread().interrupt(); } System.exit(0); } }; thread.setDaemon(true); thread.start(); } } ================================================ FILE: feature/exitonstop/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Exit on Stop Feature. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.feature.exitonstop { exports cloud.piranha.feature.exitonstop; opens cloud.piranha.feature.exitonstop; requires cloud.piranha.feature.impl; } ================================================ FILE: feature/exitonstop/src/test/java/cloud/piranha/feature/exitonstop/ExitOnStopFeatureTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.exitonstop; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the ExitOnStopFeature class. * * @author Manfred Riem (mriem@manorrock.com) */ class ExitOnStopFeatureTest { /** * Test stop method. * * @throws Exception when an error occurs. */ @Test void testStop() throws Exception { ExitOnStopFeature feature = new ExitOnStopFeature(); feature.stop(); Thread.sleep(1000); assertTrue(feature.isExiting()); } } ================================================ FILE: feature/http/pom.xml ================================================ 4.0.0 cloud.piranha.feature project 25.4.0-SNAPSHOT piranha-feature-http jar Piranha - Feature - HTTP cloud.piranha.feature piranha-feature-impl ${project.version} compile cloud.piranha.http piranha-http-impl ${project.version} compile org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test ================================================ FILE: feature/http/src/main/java/cloud/piranha/feature/http/HttpFeature.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.http; import cloud.piranha.feature.impl.DefaultFeature; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.impl.DefaultHttpServer; import java.lang.System.Logger; import static java.lang.System.Logger.Level.ERROR; import java.lang.reflect.InvocationTargetException; /** * The HTTP feature that exposes an HTTP endpoint. * * @author Manfred Riem (mriem@manorrock.com) */ public class HttpFeature extends DefaultFeature { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(HttpFeature.class.getName()); /** * Stores the HTTP server. */ private HttpServer httpServer; /** * Stores the HTTP server class. */ private String httpServerClass = DefaultHttpServer.class.getName(); /** * Stores the port. */ private int port = 8080; /** * Constructor. */ public HttpFeature() { } @Override public void destroy() { httpServer = null; } /** * Get the HTTP server. * * @return the HTTP server. */ public HttpServer getHttpServer() { return httpServer; } /** * Get the HTTP server class. * * @return the HTTP server class. */ public String getHttpServerClass() { return httpServerClass; } /** * Get the port. * * @return the port. */ public int getPort() { return port; } @Override public void init() { if (port > 0) { try { httpServer = (HttpServer) Class.forName(httpServerClass) .getDeclaredConstructor().newInstance(); } catch (ClassNotFoundException | IllegalAccessException | IllegalArgumentException | InstantiationException | NoSuchMethodException | SecurityException | InvocationTargetException t) { LOGGER.log(ERROR, "Unable to construct HTTP server", t); } if (httpServer != null) { httpServer.setServerPort(port); } } } /** * Set the HTTP server. * * @param httpServer the HTTP server. */ public void setHttpServer(HttpServer httpServer) { this.httpServer = httpServer; } /** * Set the HTTP server class. * * @param httpServerClass the HTTP server class. */ public void setHttpServerClass(String httpServerClass) { if (httpServerClass != null) { this.httpServerClass = httpServerClass; } else { this.httpServerClass = DefaultHttpServer.class.getName(); } } /** * Set the port. * * @param port the port. */ public void setPort(int port) { this.port = port; } @Override public void start() { if (httpServer != null) { httpServer.start(); } } @Override public void stop() { if (httpServer != null) { httpServer.stop(); } } } ================================================ FILE: feature/http/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the HTTP Feature. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.feature.http { exports cloud.piranha.feature.http; opens cloud.piranha.feature.http; requires transitive cloud.piranha.feature.impl; requires transitive cloud.piranha.http.impl; } ================================================ FILE: feature/http/src/test/java/cloud/piranha/feature/http/HttpFeatureTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.http; import cloud.piranha.http.api.HttpServer; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the HttpFeature class. * * @author Manfred Riem (mriem@manorrock.com) */ class HttpFeatureTest { /** * Test destroy method. */ @Test void testDestroy() { HttpFeature feature = new HttpFeature(); feature.init(); assertNotNull(feature.getHttpServer()); feature.destroy(); assertNull(feature.getHttpServer()); feature.setHttpServerClass("BOGUS"); feature.init(); assertNull(feature.getHttpServer()); feature.destroy(); assertNull(feature.getHttpServer()); } /** * Test getHttpServer method. */ @Test void testGetHttpServerMethod() { HttpFeature feature = new HttpFeature(); feature.init(); HttpServer httpsServer = feature.getHttpServer(); feature.setHttpServer(null); assertNull(feature.getHttpServer()); feature.setHttpServer(httpsServer); assertEquals(httpsServer, feature.getHttpServer()); } /** * Test getHttpServerClass method. */ @Test void testGetHttpServerClass() { HttpFeature feature = new HttpFeature(); feature.setHttpServerClass("BOGUS"); assertEquals("BOGUS", feature.getHttpServerClass()); feature.setHttpServerClass(null); assertNotNull(feature.getHttpServerClass()); } /** * Test getPort method. */ @Test void testGetPort() { HttpFeature feature = new HttpFeature(); feature.setPort(1234); assertEquals(1234, feature.getPort()); } /** * Test stop method. */ @Test void testStop() { HttpFeature feature = new HttpFeature(); feature.init(); assertFalse(feature.getHttpServer().isRunning()); feature.start(); assertTrue(feature.getHttpServer().isRunning()); feature.stop(); assertFalse(feature.getHttpServer().isRunning()); } } ================================================ FILE: feature/https/pom.xml ================================================ 4.0.0 cloud.piranha.feature project 25.4.0-SNAPSHOT piranha-feature-https jar Piranha - Feature - HTTPS cloud.piranha.feature piranha-feature-impl ${project.version} compile cloud.piranha.http piranha-http-impl ${project.version} compile org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test ================================================ FILE: feature/https/src/main/java/cloud/piranha/feature/https/HttpsFeature.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.https; import cloud.piranha.feature.impl.DefaultFeature; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.impl.DefaultHttpServer; import java.lang.System.Logger; import static java.lang.System.Logger.Level.ERROR; import java.lang.reflect.InvocationTargetException; /** * The HTTPS feature that exposes an HTTPS endpoint. * * @author Manfred Riem (mriem@manorrock.com) */ public class HttpsFeature extends DefaultFeature { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(HttpsFeature.class.getName()); /** * Stores the HTTPS server. */ private HttpServer httpsServer; /** * Stores the HTTPS server class. */ private String httpsServerClass = DefaultHttpServer.class.getName(); /** * Stores the HTTPS port. */ private int port = 8043; /** * Constructor. */ public HttpsFeature() { } @Override public void destroy() { httpsServer = null; } /** * Get the HTTPS keystore file. * * @return the HTTPS keystore file. */ public String getHttpsKeystoreFile() { return System.getProperty("javax.net.ssl.keyStore"); } /** * Get the HTTPS keystore password. * * @return the HTTPS keystore password. */ public String getHttpsKeystorePassword() { return System.getProperty("javax.net.ssl.keyStorePassword"); } /** * Get the HTTPS server. * * @return the HTTPS server. */ public HttpServer getHttpsServer() { return httpsServer; } /** * Get the HTTPS server class. * * @return the HTTPS server class. */ public String getHttpsServerClass() { return httpsServerClass; } /** * Get the HTTPS truststore file. * * @return the HTTPS truststore file. */ public String getHttpsTruststoreFile() { return System.getProperty("javax.net.ssl.trustStore"); } /** * Get the HTTPS truststore password. * * @return the HTTPS truststore password. */ public String getHttpsTruststorePassword() { return System.getProperty("javax.net.ssl.trustStorePassword"); } /** * Get the port. * * @return the port. */ public int getPort() { return port; } @Override public void init() { if (port > 0) { try { httpsServer = (HttpServer) Class.forName(httpsServerClass) .getDeclaredConstructor().newInstance(); } catch (ClassNotFoundException | IllegalAccessException | IllegalArgumentException | InstantiationException | NoSuchMethodException | SecurityException | InvocationTargetException t) { LOGGER.log(ERROR, "Unable to construct HTTP server", t); } if (httpsServer != null) { httpsServer.setServerPort(port); httpsServer.setSSL(true); } } } /** * Set the HTTPS keystore file. * *

* This is currently a convenience wrapper around the * javax.net.ssl.keyStore system property. Note using this * method sets the property for the entire JVM. *

* * @param httpsKeystoreFile the HTTPS keystore file. */ public void setHttpsKeystoreFile(String httpsKeystoreFile) { if (httpsKeystoreFile != null) { System.setProperty("javax.net.ssl.keyStore", httpsKeystoreFile); } } /** * Set the HTTPS keystore password. * *

* This is currently a convenience wrapper around the * javax.net.ssl.keyStorePassword system property. Note using * this method sets the property for the entire JVM. *

* * @param httpsKeystorePassword the HTTPS keystore password. */ public void setHttpsKeystorePassword(String httpsKeystorePassword) { if (httpsKeystorePassword != null) { System.setProperty("javax.net.ssl.keyStorePassword", httpsKeystorePassword); } } /** * Set the HTTPS server. * * @param httpsServer the HTTPS server. */ public void setHttpsServer(HttpServer httpsServer) { this.httpsServer = httpsServer; } /** * Set the HTTP server class. * * @param httpsServerClass the HTTP server class. */ public void setHttpsServerClass(String httpsServerClass) { if (httpsServerClass != null) { this.httpsServerClass = httpsServerClass; } else { this.httpsServerClass = DefaultHttpServer.class.getName(); } } /** * Set the HTTPS truststore file. * *

* This is currently a convenience wrapper around the * javax.net.ssl.trustStore system property. Note using this * method sets the property for the entire JVM. *

* * @param httpsTruststoreFile the HTTPS truststore file. */ public void setHttpsTruststoreFile(String httpsTruststoreFile) { if (httpsTruststoreFile != null) { System.setProperty("javax.net.ssl.trustStore", httpsTruststoreFile); } } /** * Set the HTTPS truststore password. * *

* This is currently a convenience wrapper around the * javax.net.ssl.trustStorePassword system property. Note using * this method sets the property for the entire JVM. *

* * @param httpsTruststorePassword the HTTPS truststore password. */ public void setHttpsTruststorePassword(String httpsTruststorePassword) { if (httpsTruststorePassword != null) { System.setProperty("javax.net.ssl.trustStorePassword", httpsTruststorePassword); } } /** * Set the port. * * @param port the port. */ public void setPort(int port) { this.port = port; } @Override public void start() { if (httpsServer != null) { httpsServer.start(); } } @Override public void stop() { if (httpsServer != null) { httpsServer.stop(); } } } ================================================ FILE: feature/https/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the HTTPS Feature. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.feature.https { exports cloud.piranha.feature.https; opens cloud.piranha.feature.https; requires transitive cloud.piranha.feature.impl; requires transitive cloud.piranha.http.impl; } ================================================ FILE: feature/https/src/test/java/cloud/piranha/feature/https/HttpsFeatureTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.https; import cloud.piranha.http.api.HttpServer; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the HttpsFeature class. * * @author Manfred Riem (mriem@manorrock.com) */ class HttpsFeatureTest { /** * Test destroy method. */ @Test void testDestroy() { HttpsFeature feature = new HttpsFeature(); feature.init(); assertNotNull(feature.getHttpsServer()); feature.destroy(); assertNull(feature.getHttpsServer()); feature.setHttpsServerClass("BOGUS"); feature.init(); assertNull(feature.getHttpsServer()); feature.destroy(); assertNull(feature.getHttpsServer()); } /** * Test getHttpsKeystoreFile method. */ @Test void testGetHttpsKeystoreFile() { HttpsFeature feature = new HttpsFeature(); feature.setHttpsKeystoreFile("keystore"); assertEquals("keystore", feature.getHttpsKeystoreFile()); } /** * Test getHttpsKeystorePassword method. */ @Test public void testGetHttpsKeystorePassword() { HttpsFeature feature = new HttpsFeature(); feature.setHttpsKeystorePassword("password"); assertEquals("password", feature.getHttpsKeystorePassword()); } /** * Test getHttpServer method. */ @Test void testGetHttpServerMethod() { HttpsFeature feature = new HttpsFeature(); feature.init(); HttpServer httpsServer = feature.getHttpsServer(); feature.setHttpsServer(null); assertNull(feature.getHttpsServer()); feature.setHttpsServer(httpsServer); assertEquals(httpsServer, feature.getHttpsServer()); } /** * Test getHttpServerClass method. */ @Test void testGetHttpServerClass() { HttpsFeature feature = new HttpsFeature(); feature.setHttpsServerClass("BOGUS"); assertEquals("BOGUS", feature.getHttpsServerClass()); feature.setHttpsServerClass(null); assertNotNull(feature.getHttpsServerClass()); } /** * Test getHttpsTruststoreFile method. */ @Test void testGetHttpsTruststoreFile() { HttpsFeature feature = new HttpsFeature(); feature.setHttpsTruststoreFile("truststore"); assertEquals("truststore", feature.getHttpsTruststoreFile()); } /** * Test getHttpsTruststorePassword method. */ @Test void testGetHttpsTruststorePassword() { HttpsFeature feature = new HttpsFeature(); feature.setHttpsTruststorePassword("password"); assertEquals("password", feature.getHttpsTruststorePassword()); } /** * Test getPort method. */ @Test void testGetPort() { HttpsFeature feature = new HttpsFeature(); feature.setPort(1234); assertEquals(1234, feature.getPort()); } /** * Test stop method. */ @Test void testStop() { HttpsFeature feature = new HttpsFeature(); feature.init(); assertFalse(feature.getHttpsServer().isRunning()); feature.start(); assertTrue(feature.getHttpsServer().isRunning()); feature.stop(); assertFalse(feature.getHttpsServer().isRunning()); } } ================================================ FILE: feature/impl/pom.xml ================================================ 4.0.0 cloud.piranha.feature project 25.4.0-SNAPSHOT piranha-feature-impl jar Piranha - Feature - Implementation cloud.piranha.feature piranha-feature-api ${project.version} compile org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test ================================================ FILE: feature/impl/src/main/java/cloud/piranha/feature/impl/DefaultFeature.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.impl; import cloud.piranha.feature.api.Feature; import cloud.piranha.feature.api.FeatureManager; /** * The default Feature implementation. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultFeature implements Feature { /** * Stores the feature manager. */ protected FeatureManager featureManager; /** * Constructor. */ public DefaultFeature() { } @Override public FeatureManager getFeatureManager() { return featureManager; } @Override public void setFeatureManager(FeatureManager featureManager) { this.featureManager = featureManager; } } ================================================ FILE: feature/impl/src/main/java/cloud/piranha/feature/impl/DefaultFeatureManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.impl; import cloud.piranha.feature.api.Feature; import cloud.piranha.feature.api.FeatureManager; import java.util.ArrayList; import java.util.List; /** * The default Feature manager. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultFeatureManager implements FeatureManager { /** * Stores the features. */ private final ArrayList features = new ArrayList<>(); /** * Constructor. */ public DefaultFeatureManager() { } @Override public void addFeature(Feature feature) { features.add(feature); feature.setFeatureManager(this); } @Override public List getFeatures() { return features; } @Override public void stop() { features.forEach(f -> f.stop()); } } ================================================ FILE: feature/impl/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the default Feature implementations. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.feature.impl { exports cloud.piranha.feature.impl; opens cloud.piranha.feature.impl; requires transitive cloud.piranha.feature.api; } ================================================ FILE: feature/impl/src/test/java/cloud/piranha/feature/impl/DefaultFeatureManagerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.impl; import cloud.piranha.feature.api.Feature; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.Test; /** * The JUnit tests for the DefaultFeatureManager class. * * @author Manfred Riem (mriem@manorrock.com) */ class DefaultFeatureManagerTest { /** * Test addFeature method. */ @Test void testAddFeature() { DefaultFeatureManager manager = new DefaultFeatureManager(); Feature feature = new DefaultFeature(); manager.addFeature(feature); assertEquals(feature, manager.getFeatures().get(0)); } /** * Test stop method. */ @Test void testStop() { DefaultFeatureManager manager = new DefaultFeatureManager(); manager.addFeature(new DefaultFeature() { @Override public void stop() { System.setProperty("DefaultFeatureManager.stop", "true"); } }); manager.stop(); assertNotNull(System.getProperty("DefaultFeatureManager.stop")); } } ================================================ FILE: feature/impl/src/test/java/cloud/piranha/feature/impl/DefaultFeatureTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.impl; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; /** * The JUnit tests for the DefaultFeature class. * * @author Manfred Riem (mriem@manorrock.com) */ class DefaultFeatureTest { /** * Test getFeatureManager method */ @Test void testGetFeatureManager() { DefaultFeature feature = new DefaultFeature(); assertNull(feature.getFeatureManager()); DefaultFeatureManager manager = new DefaultFeatureManager(); feature.setFeatureManager(manager); assertEquals(manager, feature.getFeatureManager()); } } ================================================ FILE: feature/isolatedwebapp/pom.xml ================================================ 4.0.0 cloud.piranha.feature project 25.4.0-SNAPSHOT piranha-feature-isolatedwebapp jar Piranha - Feature - Isolated WebApp cloud.piranha.feature piranha-feature-webapp ${project.version} compile cloud.piranha.micro piranha-micro-builder ${project.version} compile cloud.piranha.micro piranha-micro-loader ${project.version} compile org.jboss.shrinkwrap.descriptors shrinkwrap-descriptors-api-base compile true org.jboss.shrinkwrap.resolver shrinkwrap-resolver-api-maven compile true org.jboss.shrinkwrap.resolver shrinkwrap-resolver-impl-maven compile true org.jboss.shrinkwrap.resolver shrinkwrap-resolver-impl-maven-archive compile true org.jboss.shrinkwrap.resolver shrinkwrap-resolver-spi compile true org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test ================================================ FILE: feature/isolatedwebapp/src/main/java/cloud/piranha/feature/isolatedwebapp/IsolatedWebAppFeature.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.isolatedwebapp; import cloud.piranha.feature.impl.DefaultFeature; import cloud.piranha.http.webapp.HttpWebApplicationServer; import cloud.piranha.micro.builder.MicroWebApplication; import cloud.piranha.micro.loader.MicroConfiguration; import cloud.piranha.micro.loader.MicroOuterDeployer; import java.io.File; import java.lang.System.Logger; import static java.lang.System.Logger.Level.ERROR; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.importer.ZipImporter; import org.jboss.shrinkwrap.api.spec.WebArchive; /** * The Isolated WebApp feature. * * @author Manfred Riem (mriem@manorrock.com) */ public class IsolatedWebAppFeature extends DefaultFeature { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(IsolatedWebAppFeature.class.getName()); /** * Stores the HttpWebApplicationServer. */ private HttpWebApplicationServer httpWebApplicationServer; /** * Stores the WAR file. */ private File warFile; /** * Constructor. */ public IsolatedWebAppFeature() { } /** * Get the HttpWebApplicationServer. * * @return the HttpWebApplicationServer. */ public HttpWebApplicationServer getHttpWebApplicationServer() { return httpWebApplicationServer; } /** * Get the context path. * * @param warFile the WAR file. * @return the context path. */ private String getContextPath(File warFile) { String contextPath = warFile.getName().substring(0, warFile.getName().length() - 4); if (contextPath.equalsIgnoreCase("ROOT")) { contextPath = ""; } else if (!contextPath.startsWith("/")) { contextPath = "/" + contextPath; } return contextPath; } /** * Get the WAR file. * * @return the WAR file. */ public File getWarFile() { return warFile; } @Override public void init() { deploy(warFile, httpWebApplicationServer); } private void deploy(File warFile, HttpWebApplicationServer webApplicationServer) { String contextPath = getContextPath(warFile); MicroConfiguration configuration = new MicroConfiguration(); configuration.setContextPath(contextPath); configuration.setHttpStart(false); try { MicroWebApplication microWebApplication = new MicroWebApplication(); microWebApplication.setContextPath(contextPath); microWebApplication.setDeployedApplication( new MicroOuterDeployer(configuration.postConstruct()) .deploy(ShrinkWrap.create(ZipImporter.class, warFile.getName()).importFrom(warFile).as(WebArchive.class)) .getDeployedApplication()); webApplicationServer.addWebApplication(microWebApplication); } catch (Exception e) { LOGGER.log(ERROR, () -> "Failed to initialize web application at " + contextPath, e); } } /** * Set the HttpWebApplicationServer. * * @param httpWebApplicationServer the HttpWebApplicationServer. */ public void setHttpWebApplicationServer(HttpWebApplicationServer httpWebApplicationServer) { this.httpWebApplicationServer = httpWebApplicationServer; } /** * Set the WAR file. * * @param warFile the WAR file. */ public void setWarFile(File warFile) { this.warFile = warFile; } } ================================================ FILE: feature/isolatedwebapp/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Isolated WebApp Feature. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.feature.isolatedwebapp { exports cloud.piranha.feature.isolatedwebapp; opens cloud.piranha.feature.isolatedwebapp; requires cloud.piranha.feature.impl; requires transitive cloud.piranha.http.webapp; requires cloud.piranha.micro.builder; requires cloud.piranha.micro.loader; requires shrinkwrap.api; requires shrinkwrap.resolver.api.maven; } ================================================ FILE: feature/logging/pom.xml ================================================ 4.0.0 cloud.piranha.feature project 25.4.0-SNAPSHOT piranha-feature-logging jar Piranha - Feature - Logging cloud.piranha.feature piranha-feature-impl ${project.version} compile org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test ================================================ FILE: feature/logging/src/main/java/cloud/piranha/feature/logging/LoggingFeature.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.logging; import java.util.logging.ConsoleHandler; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.Logger; import cloud.piranha.feature.impl.DefaultFeature; /** * The Logging feature. * * @author Manfred Riem (mriem@manorrock.com) */ public class LoggingFeature extends DefaultFeature { /** * Stores the logger. */ private static final java.lang.System.Logger LOGGER = System.getLogger(LoggingFeature.class.getName()); /** * Stores the logging level. */ private String level; /** * Constructor. */ public LoggingFeature() { } /** * Get the logging level. * * @return the logging level. */ public String getLevel() { return level; } @Override public void init() { /* * Remote the default console handler. */ Logger rootLogger = Logger.getLogger(""); Handler[] handlers = rootLogger.getHandlers(); if (handlers[0] instanceof ConsoleHandler) { rootLogger.removeHandler(handlers[0]); } /* * Add our custom console handler. */ LoggingHandler loggingHandler = new LoggingHandler(); rootLogger.addHandler(loggingHandler); rootLogger.setLevel(Level.ALL); /** * Set the log level (if set). */ if (level != null) { Logger logger = LogManager.getLogManager().getLogger(""); logger.setLevel(Level.parse(level)); handlers = logger.getHandlers(); for(int i=0; i future = executorService.submit(() -> consoleHandler.publish(record)); try { future.get(10, TimeUnit.SECONDS); } catch (TimeoutException e) { future.cancel(true); } catch (Exception e) { // Handle other exceptions } } } @Override public void flush() { consoleHandler.flush(); } @Override public void close() throws SecurityException { consoleHandler.close(); executorService.shutdown(); } } ================================================ FILE: feature/logging/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the LoggingFeature. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.feature.logging { exports cloud.piranha.feature.logging; opens cloud.piranha.feature.logging; requires transitive cloud.piranha.feature.impl; requires transitive java.logging; } ================================================ FILE: feature/logging/src/test/java/cloud/piranha/feature/logging/LoggingFeatureTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.logging; import static java.util.logging.Level.ALL; import static java.util.logging.Level.SEVERE; import java.util.logging.LogManager; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import org.junit.jupiter.api.Test; /** * The JUnit tests for the LoggingFeature class. * * @author Manfred Riem (mriem@manorrock.com) */ class LoggingFeatureTest { /** * Test getLevel method. */ @Test void testGetLevel() { LoggingFeature feature = new LoggingFeature(); assertNull(feature.getLevel()); feature.setLevel("SEVERE"); assertEquals("SEVERE", feature.getLevel()); } /** * Test init method */ @Test void testInit() { LoggingFeature feature = new LoggingFeature(); feature.init(); assertEquals(ALL, LogManager.getLogManager().getLogger("").getLevel()); feature = new LoggingFeature(); feature.setLevel("SEVERE"); feature.init(); assertEquals(SEVERE, LogManager.getLogManager().getLogger("").getLevel()); } } ================================================ FILE: feature/pid/pom.xml ================================================ 4.0.0 cloud.piranha.feature project 25.4.0-SNAPSHOT piranha-feature-pid jar Piranha - Feature - PID cloud.piranha.feature piranha-feature-impl ${project.version} compile ================================================ FILE: feature/pid/src/main/java/cloud/piranha/feature/pid/PidFeature.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.pid; import cloud.piranha.feature.impl.DefaultFeature; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import static java.lang.System.Logger.Level.WARNING; /** * The PID feature. * * @author Manfred Riem (mriem@manorrock.com) */ public class PidFeature extends DefaultFeature implements Runnable { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(PidFeature.class.getName()); /** * Stores the PID. */ private Long pid; /** * Stores the stop flag. */ private boolean stop = false; /** * Stores the thread. */ private Thread thread; /** * Constructor. */ public PidFeature() { } /** * Get the PID. * * @return the PID. */ public Long getPid() { return pid; } /** * Get the thread. * * @return the thread. */ public Thread getThread() { return thread; } @Override public void run() { while (!stop) { if (pid != null) { File pidFile = new File("tmp", "piranha.pid"); if (!pidFile.exists()) { stop(); } } try { Thread.sleep(2000); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } } /** * Set the PID. * * @param pid the PID. */ public void setPid(Long pid) { this.pid = pid; } @Override public void start() { stop = false; thread = new Thread(this); thread.start(); File pidFile = new File("tmp", "piranha.pid"); if (!pidFile.getParentFile().exists() && !pidFile.getParentFile().mkdirs()) { LOGGER.log(WARNING, "Unable to create tmp directory for PID file"); } try (PrintWriter writer = new PrintWriter(new FileWriter(pidFile))) { writer.println(pid); writer.flush(); } catch (IOException ioe) { LOGGER.log(WARNING, "Unable to write PID file", ioe); } } @Override public void stop() { stop = true; thread = null; featureManager.stop(); } } ================================================ FILE: feature/pid/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the PID Feature. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.feature.pid { exports cloud.piranha.feature.pid; opens cloud.piranha.feature.pid; requires transitive cloud.piranha.feature.impl; } ================================================ FILE: feature/pom.xml ================================================ 4.0.0 cloud.piranha project 25.4.0-SNAPSHOT cloud.piranha.feature project pom Piranha - Feature api crac exitonstop http https impl isolatedwebapp logging pid trace webapp webapps cloud.piranha bom ${project.version} pom import org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test default file:///tmp/piranha/feature/ ================================================ FILE: feature/trace/pom.xml ================================================ 4.0.0 cloud.piranha.feature project 25.4.0-SNAPSHOT piranha-feature-trace jar Piranha - Feature - Trace cloud.piranha.core piranha-core-impl ${project.version} compile cloud.piranha.extension piranha-extension-fileupload ${project.version} compile true cloud.piranha.feature piranha-feature-impl ${project.version} compile cloud.piranha.http piranha-http-webapp ${project.version} compile org.junit.jupiter junit-jupiter-api test ================================================ FILE: feature/trace/src/main/java/cloud/piranha/feature/trace/TraceFeature.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.trace; import cloud.piranha.core.impl.DefaultFilterChain; import cloud.piranha.core.impl.DefaultInvocationFinder; import cloud.piranha.core.impl.DefaultServletRequestManager; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.core.impl.DefaultWebApplicationInputStream; import cloud.piranha.core.impl.DefaultWebApplicationOutputStream; import cloud.piranha.extension.fileupload.FileUploadMultiPartManager; import cloud.piranha.feature.impl.DefaultFeature; import cloud.piranha.http.webapp.HttpWebApplicationServer; import cloud.piranha.http.webapp.HttpWebApplicationServerRequestMapper; import static java.util.logging.Level.FINEST; import java.util.logging.Logger; /** * The Trace feature. * * @author Manfred Riem (mriem@manorrock.com) */ public class TraceFeature extends DefaultFeature { /** * Constructor. */ public TraceFeature() { } @Override public void init() { boolean enabled = Boolean.parseBoolean("CLOUD_PIRANHA_FEATURE_TRACE_ENABLED"); if (!enabled) { enabled = Boolean.parseBoolean( System.getProperty("cloud.piranha.feature.trace.enabled", "false")); } if (enabled) { Logger logger = Logger.getLogger(DefaultFilterChain.class.getName()); logger.setLevel(FINEST); logger = Logger.getLogger(DefaultInvocationFinder.class.getName()); logger.setLevel(FINEST); logger = Logger.getLogger(DefaultServletRequestManager.class.getName()); logger.setLevel(FINEST); logger = Logger.getLogger(DefaultWebApplication.class.getName()); logger.setLevel(FINEST); logger = Logger.getLogger(DefaultWebApplication.class.getName()); logger.setLevel(FINEST); logger = Logger.getLogger(DefaultWebApplicationInputStream.class.getName()); logger.setLevel(FINEST); logger = Logger.getLogger(DefaultWebApplicationOutputStream.class.getName()); logger.setLevel(FINEST); logger = Logger.getLogger(HttpWebApplicationServer.class.getName()); logger.setLevel(FINEST); logger = Logger.getLogger(HttpWebApplicationServerRequestMapper.class.getName()); logger.setLevel(FINEST); /* * The following uses a try / catch as the extension is optionally * available depending on the runtime. */ try { logger = Logger.getLogger(FileUploadMultiPartManager.class.getName()); logger.setLevel(FINEST); } catch(Throwable t) { // swallow up } } } } ================================================ FILE: feature/trace/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the TraceFeature. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.feature.trace { exports cloud.piranha.feature.trace; opens cloud.piranha.feature.trace; requires transitive cloud.piranha.core.impl; requires static cloud.piranha.extension.fileupload; requires transitive cloud.piranha.feature.impl; requires transitive cloud.piranha.http.webapp; requires transitive java.logging; } ================================================ FILE: feature/trace/src/test/java/cloud/piranha/feature/trace/tests/TraceFeatureTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.trace.tests; import cloud.piranha.core.impl.DefaultFilterChain; import cloud.piranha.feature.trace.TraceFeature; import java.lang.System.Logger; import static java.lang.System.Logger.Level.TRACE; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the TraceFeature class. * * @author Manfred Riem (mriem@manorrock.com) */ class TraceFeatureTest { /** * Test init method */ @Test void testInit() { TraceFeature feature = new TraceFeature(); feature.init(); } } ================================================ FILE: feature/trace/src/test/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the tests for the TraceFeature. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.feature.trace.tests { exports cloud.piranha.feature.trace.tests; opens cloud.piranha.feature.trace.tests; requires cloud.piranha.feature.trace; requires org.junit.jupiter.api; } ================================================ FILE: feature/webapp/pom.xml ================================================ 4.0.0 cloud.piranha.feature project 25.4.0-SNAPSHOT piranha-feature-webapp jar Piranha - Feature - WebApp cloud.piranha.feature piranha-feature-impl ${project.version} compile cloud.piranha.http piranha-http-webapp ${project.version} compile org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test ================================================ FILE: feature/webapp/src/main/java/cloud/piranha/feature/webapp/WebAppFeature.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.webapp; import cloud.piranha.core.api.WebApplicationExtension; import cloud.piranha.core.impl.DefaultModuleFinder; import cloud.piranha.core.impl.DefaultModuleLayerProcessor; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.core.impl.DefaultWebApplicationClassLoader; import cloud.piranha.core.impl.DefaultWebApplicationExtensionContext; import static cloud.piranha.core.impl.WarFileExtractor.extractWarFile; import cloud.piranha.feature.impl.DefaultFeature; import cloud.piranha.http.api.HttpServerProcessor; import cloud.piranha.http.webapp.HttpWebApplicationServer; import cloud.piranha.resource.impl.DirectoryResource; import java.io.File; import static java.lang.System.Logger.Level.ERROR; import java.lang.module.Configuration; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleFinder; import java.lang.module.ModuleReference; import java.util.List; import java.util.ServiceLoader; /** * The Web Application feature. * *

* The Web Application feature delivers the capability to host a single web * application. *

* * @author Manfred Riem (mriem@manorrock.com) */ public class WebAppFeature extends DefaultFeature { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(WebAppFeature.class.getName()); /** * Stores the context path. */ private String contextPath; /** * Stores the extension class. */ private Class extensionClass; /** * Stores the JPMS enabled flag. */ private boolean jpmsEnabled; /** * Stores the war file. */ private File warFile; /** * Stores the web application directory. */ private File webAppDir; /** * Stores the HTTP web application server. */ private HttpWebApplicationServer httpWebApplicationServer; /** * Constructor. */ public WebAppFeature() { } /** * Get the context path. * * @return the context path. */ public String getContextPath() { return contextPath; } /** * Get the extension class. * * @return the extension class. */ public Class getExtensionClass() { return extensionClass; } /** * Get the HttpServerProcessor. * * @return the HttpServerProcessor. */ public HttpServerProcessor getHttpServerProcessor() { return httpWebApplicationServer; } /** * Get the HttpWebApplicationServer. * * @return the HttpWebApplicationServer. */ public HttpWebApplicationServer getHttpWebApplicationServer() { return httpWebApplicationServer; } /** * Get the WAR file. * * @return the WAR file. */ public File getWarFile() { return warFile; } /** * Get the web application directory. * * @return the web application directory. */ public File getWebAppDir() { return webAppDir; } @Override public void init() { if (httpWebApplicationServer == null) { httpWebApplicationServer = new HttpWebApplicationServer(); } if (warFile != null && warFile.getName().toLowerCase().endsWith(".war")) { if (contextPath == null) { contextPath = warFile.getName().substring(0, warFile.getName().length() - 4); } if (webAppDir == null) { webAppDir = new File(contextPath); } extractWarFile(warFile, webAppDir); } if (webAppDir != null && webAppDir.exists()) { if (contextPath == null) { contextPath = webAppDir.getName(); } DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.addResource(new DirectoryResource(webAppDir)); DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(webAppDir); webApplication.setClassLoader(classLoader); if (jpmsEnabled) { setupLayers(classLoader); } if (classLoader.getResource("/META-INF/services/" + WebApplicationExtension.class.getName()) == null) { DefaultWebApplicationExtensionContext extensionContext = new DefaultWebApplicationExtensionContext(); extensionContext.add((extensionClass)); extensionContext.configure(webApplication); } else { DefaultWebApplicationExtensionContext extensionContext = new DefaultWebApplicationExtensionContext(); ServiceLoader serviceLoader = ServiceLoader.load(WebApplicationExtension.class, classLoader); extensionContext.add(serviceLoader.iterator().next()); extensionContext.configure(webApplication); } if (contextPath.equalsIgnoreCase("ROOT")) { contextPath = ""; } else if (!contextPath.startsWith("/")) { contextPath = "/" + contextPath; } webApplication.setContextPath(contextPath); httpWebApplicationServer.addWebApplication(webApplication); try { webApplication.initialize(); } catch (Throwable e) { LOGGER.log(ERROR, "Failed to initialize web application"); } } if (webAppDir == null && warFile == null) { LOGGER.log(ERROR, "No web application deployed"); } } /** * Get the JPMS enabled flag. * * @return true if JPMS should be enabled, false otherwise. */ public boolean isJpmsEnabled() { return jpmsEnabled; } /** * Set the context path. * * @param contextPath the context path. */ public void setContextPath(String contextPath) { this.contextPath = contextPath; } /** * Set the extension class. * * @param extensionClass the extension class. */ public void setExtensionClass(Class extensionClass) { this.extensionClass = extensionClass; } /** * Set the HttpWebApplicationServer. * * @param httpWebApplicationServer the HttpWebApplicationServer. */ public void setHttpWebApplicationServer(HttpWebApplicationServer httpWebApplicationServer) { this.httpWebApplicationServer = httpWebApplicationServer; } /** * Set the JPMS enabled flag. * * @param jpmsEnabled the JPMS enabled flag. */ public void setJpmsEnabled(boolean jpmsEnabled) { this.jpmsEnabled = jpmsEnabled; } /** * Set the WAR file. * * @param warFile the WAR file. */ public void setWarFile(File warFile) { if (warFile != null) { this.warFile = warFile; } } /** * Set the web application directory. * * @param webAppDir the web application directory. */ public void setWebAppDir(File webAppDir) { this.webAppDir = webAppDir; } /** * Setup the layers. * * @param classLoader the web application class loader. */ private void setupLayers(DefaultWebApplicationClassLoader classLoader) { ModuleFinder defaultModuleFinder = new DefaultModuleFinder(classLoader.getResourceManager().getResourceList()); List roots = defaultModuleFinder.findAll() .stream() .map(ModuleReference::descriptor) .map(ModuleDescriptor::name) .toList(); if (!roots.isEmpty()) { Configuration configuration = ModuleLayer.boot().configuration().resolveAndBind(defaultModuleFinder, ModuleFinder.of(), roots); ModuleLayer.Controller controller = ModuleLayer.defineModules(configuration, List.of(ModuleLayer.boot()), x -> classLoader); DefaultModuleLayerProcessor.INSTANCE.processModuleLayerOptions(controller); } } @Override public void start() { httpWebApplicationServer.start(); } @Override public void stop() { httpWebApplicationServer.stop(); } } ================================================ FILE: feature/webapp/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Web Application Feature. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.feature.webapp { exports cloud.piranha.feature.webapp; opens cloud.piranha.feature.webapp; requires transitive cloud.piranha.feature.impl; requires transitive cloud.piranha.http.webapp; } ================================================ FILE: feature/webapp/src/test/java/cloud/piranha/feature/webapp/WebAppFeatureTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.webapp; import cloud.piranha.core.api.WebApplicationExtension; import java.io.File; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import org.junit.jupiter.api.Test; /** * The JUnit tests for the WebAppFeature class. * * @author Manfred Riem (mriem@manorrock.com) */ class WebAppFeatureTest { /** * Test getExtensionClass method. */ @Test void testGetExtensionClass() { WebAppFeature feature = new WebAppFeature(); assertNull(feature.getExtensionClass()); feature.setExtensionClass(WebApplicationExtension.class); assertNotNull(feature.getExtensionClass()); } /** * Test getContextPath method. */ @Test void testGetContextPath() { WebAppFeature feature = new WebAppFeature(); assertNull(feature.getContextPath()); feature.setContextPath("/contextpath"); assertEquals("/contextpath", feature.getContextPath()); } /** * Test getWarFile method. */ @Test void testGetWarFile() { WebAppFeature feature = new WebAppFeature(); assertNull(feature.getWarFile()); File warFile = new File("test.war"); feature.setWarFile(warFile); assertEquals(warFile, feature.getWarFile()); } /** * Test getWebAppDir method. */ @Test void testGetWebAppDir() { WebAppFeature feature = new WebAppFeature(); assertNull(feature.getWebAppDir()); File webAppDir = new File("test"); feature.setWebAppDir(webAppDir); assertEquals(webAppDir, feature.getWebAppDir()); } } ================================================ FILE: feature/webapps/pom.xml ================================================ 4.0.0 cloud.piranha.feature project 25.4.0-SNAPSHOT piranha-feature-webapps jar Piranha - Feature - WebApps cloud.piranha.feature piranha-feature-webapp ${project.version} compile ================================================ FILE: feature/webapps/src/main/java/cloud/piranha/feature/webapps/WebAppsFeature.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.feature.webapps; import cloud.piranha.core.api.WebApplicationExtension; import static cloud.piranha.core.impl.WarFileExtractor.extractWarFile; import cloud.piranha.feature.impl.DefaultFeature; import cloud.piranha.feature.webapp.WebAppFeature; import cloud.piranha.http.api.HttpServerProcessor; import cloud.piranha.http.webapp.HttpWebApplicationServer; import java.io.File; import java.lang.System.Logger.Level; import java.util.ArrayList; /** * The WebApps feature. * *

* The WebApps feature delivers the capability to host multiple web * applications. *

* * @author Manfred Riem (mriem@manorrock.com) */ public class WebAppsFeature extends DefaultFeature { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(WebAppsFeature.class.getName()); /** * Stores the extension class. */ private Class extensionClass; /** * Stores the JPMS enabled flag. */ private boolean jpmsEnabled; /** * Stores the list of WebAppFeatures. */ private final ArrayList webAppFeatures = new ArrayList<>(); /** * Stores the web applications directory. */ private File webAppsDir; /** * Stores the HTTP web application server. */ private HttpWebApplicationServer webApplicationServer; /** * Constructor. */ public WebAppsFeature() { } /** * Get the HttpServerProcessor. * * @return the HttpServerProcessor. */ public HttpServerProcessor getHttpServerProcessor() { return webApplicationServer; } /** * Get the web applications directory. * * @return the web application directory. */ public File getWebAppsDir() { return webAppsDir; } /** * Get the HttpWebApplicationServer. * * @return the HttpWebApplicationServer. */ public HttpWebApplicationServer getHttpWebApplicationServer() { return webApplicationServer; } @Override public void init() { if (LOGGER.isLoggable(Level.DEBUG)) { LOGGER.log(Level.DEBUG, "Initializing WebAppsFeature"); } if (webApplicationServer == null) { webApplicationServer = new HttpWebApplicationServer(); } File[] webapps = webAppsDir.listFiles(); if (webapps != null) { for (File warFile : webapps) { String contextPath = null; File webAppDir = null; /* * Extract into webAppDir. */ if (warFile.getName().toLowerCase().endsWith(".war")) { contextPath = warFile.getName().substring(0, warFile.getName().length() - 4); webAppDir = new File(webAppsDir, contextPath); extractWarFile(warFile, webAppDir); } /* * Construct, init and start the WebAppFeature for the web application. */ WebAppFeature webAppFeature = new WebAppFeature(); webAppFeatures.add(webAppFeature); webAppFeature.setContextPath(contextPath); webAppFeature.setExtensionClass(extensionClass); webAppFeature.setHttpWebApplicationServer(webApplicationServer); webAppFeature.setJpmsEnabled(jpmsEnabled); webAppFeature.setWarFile(warFile); webAppFeature.setWebAppDir(webAppDir); webAppFeature.init(); } } } /** * Get the JPMS enabled flag. * * @return true if JPMS should be enabled, false otherwise. */ public boolean isJpmsEnabled() { return jpmsEnabled; } /** * Set the extension class. * * @param extensionClass the extension class. */ public void setExtensionClass(Class extensionClass) { this.extensionClass = extensionClass; } /** * Set the JPMS enabled flag. * * @param jpmsEnabled the JPMS enabled flag. */ public void setJpmsEnabled(boolean jpmsEnabled) { this.jpmsEnabled = jpmsEnabled; } /** * Set the web applications directory. * * @param webAppsDir the web applications directory. */ public void setWebAppsDir(File webAppsDir) { this.webAppsDir = webAppsDir; } @Override public void start() { webAppFeatures.forEach(f -> f.start()); webApplicationServer.start(); } @Override public void stop() { webApplicationServer.stop(); webAppFeatures.forEach(f -> f.stop()); } } ================================================ FILE: feature/webapps/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the WebApps Feature. * *

* The WebApps feature delivers the support to run multiple web applications. *

* * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.feature.webapps { exports cloud.piranha.feature.webapps; opens cloud.piranha.feature.webapps; requires transitive cloud.piranha.feature.webapp; } ================================================ FILE: http/api/pom.xml ================================================ 4.0.0 cloud.piranha.http project 25.4.0-SNAPSHOT piranha-http-api jar Piranha - HTTP - API org.junit.jupiter junit-jupiter-api test ================================================ FILE: http/api/src/main/java/cloud/piranha/http/api/HttpServer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.api; /** * The HTTP Server API. * *

* This API makes it possible to interact with an HTTP server implementation on * a very basic level. It supports the following: *

*
    *
  1. Starting the server.
  2. *
  3. Stopping the server.
  4. *
  5. Checking if the server is running.
  6. *
  7. Set/get the port of the server.
  8. *
  9. Set/get the SSL flag.
  10. *
  11. Set/get the HttpServerProcessor.
  12. *
* * @author Manfred Riem (mriem@manorrock.com) */ public interface HttpServer { /** * Check if the server is running. * * @return true if it is, false otherwise. */ boolean isRunning(); /** * Start the server. */ void start(); /** * Stop the server. */ void stop(); /*** * {@return the server port} */ int getServerPort(); /*** * Set the server port * @param serverPort the port */ void setServerPort(int serverPort); /*** * Get the SSL flag * @return the server port */ boolean getSSL(); /*** * Set the SSL flag * @param ssl the SSL flag */ void setSSL(boolean ssl); /*** * {@return the http server processor} */ HttpServerProcessor getHttpServerProcessor(); /*** * Set the http server processor * @param httpServerProcessor the http server processor */ void setHttpServerProcessor(HttpServerProcessor httpServerProcessor); } ================================================ FILE: http/api/src/main/java/cloud/piranha/http/api/HttpServerProcessor.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.api; /** * The HTTP Server Processor API. * *

* This API is defined as the main entry point for HTTP request processing. *

* * @author Manfred Riem (mriem@manorrock.com) */ public interface HttpServerProcessor { /** * Process the request. * * @param request the request. * @param response the response. * @return the end state. */ HttpServerProcessorEndState process(HttpServerRequest request, HttpServerResponse response); } ================================================ FILE: http/api/src/main/java/cloud/piranha/http/api/HttpServerProcessorEndState.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.api; /** * The end state of the HTTP processing. * * @author Manfred Riem (mriem@manorrock.com) */ public enum HttpServerProcessorEndState { /** * The processing has completed */ COMPLETED, /** * The processing has been asynced. */ ASYNCED, /** * The processing has been upgraded. */ UPGRADED } ================================================ FILE: http/api/src/main/java/cloud/piranha/http/api/HttpServerRequest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.api; import java.io.InputStream; import java.security.Principal; import java.security.cert.X509Certificate; import java.util.Iterator; /** * The HttpServerRequest API. * *

* This API delivers an abstraction over the HTTP request line, the HTTP request * headers and the HTTP request body. *

* * @author Manfred Riem (mriem@manorrock.com) */ public interface HttpServerRequest { /** * Get the header. * * @param name the name of the header. * @return the value, or null. */ String getHeader(String name); /** * Get the headers. * * @param name the name of the header. * @return the potentially empty collection. */ Iterator getHeaders(String name); /** * {@return the header names} */ Iterator getHeaderNames(); /** * {@return the input stream} */ InputStream getInputStream(); /** * {@return the local address} */ String getLocalAddress(); /** * {@return the local hostname} */ String getLocalHostname(); /** * {@return the local port} */ int getLocalPort(); /** * {@return the method} */ String getMethod(); /** * {@return the remote address} */ String getRemoteAddress(); /** * {@return the remote hostname} */ String getRemoteHostname(); /** * {@return the remote port} */ int getRemotePort(); /** * {@return the request target} */ String getRequestTarget(); /** * {@return the protocol} */ default String getProtocol() { return "HTTP/1.1"; } /** * Get the SSL certificates. * * @return the SSL certificates. */ default X509Certificate[] getSslCertificates() { return null; } /** * Get the SSL cipher suite. * * @return the SSL cipher suite. */ default String getSslCipherSuite() { return null; } /** * Get the SSL key size. * * @return the SSL key size. */ default int getSslKeySize() { return -1; } /** * Get the SSL principal. * * @return the SSL principal. */ default Principal getSslPrincipal() { return null; } /** * {@return if we are secure} */ boolean isSecure(); } ================================================ FILE: http/api/src/main/java/cloud/piranha/http/api/HttpServerResponse.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.api; import java.io.IOException; import java.io.OutputStream; /** * THe HttpServerResponse API. * *

* This API delivers an abstraction over the HTTP response status line, the HTTP * response headers and the HTTP response body. *

* * * @author Manfred Riem (mriem@manorrock.com) */ public interface HttpServerResponse { /** * Add the header. * * @param name the name. * @param value the value. */ void addHeader(String name, String value); /** * Close the response. * * @throws IOException when an I/O error occurs. */ default void closeResponse() throws IOException { getOutputStream().flush(); getOutputStream().close(); } /** * Get the header. * * @param name the name. * @return the value, or null if not found. */ String getHeader(String name); /** * {@return the output stream} */ OutputStream getOutputStream(); /** * Set the specified header. * * @param name the header name. * @param value the header value. */ void setHeader(String name, String value); /** * Set the status. * * @param status the status. */ void setStatus(int status); /** * Write the headers. * * @throws IOException when an I/O error occurs. */ void writeHeaders() throws IOException; /** * Write the status line. * * @throws IOException when an I/O error occurs. */ void writeStatusLine() throws IOException; } ================================================ FILE: http/api/src/main/java/cloud/piranha/http/api/package-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** *

* This package delivers our HTTP Server API. *

*

* This is our abstraction of a HTTP Server that is used by the various HTTP * Server implementations we support. *

* * @author Manfred Riem (mriem@manorrock.com) */ package cloud.piranha.http.api; ================================================ FILE: http/api/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Piranha HTTP API. * *

* This module delivers the API that each HTTP engine implementation needs to * implement to deliver HTTP server functionality to the Piranha project. *

* * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.http.api { exports cloud.piranha.http.api; opens cloud.piranha.http.api; } ================================================ FILE: http/crac/Dockerfile ================================================ FROM ubuntu # # Put the piranha-servlet.jar in the directory where this Dockerfile resides. # ADD piranha-dist-servlet.jar /root # # Download JDK from https://github.com/CRaC/openjdk-builds/releases and put it # in the directory where this Dockerfile resides. Adjust the filenames below if # you are using a newer release. # ADD openjdk-17-crac+3_linux-x64.tar.gz /opt ENV JAVA_HOME=/opt/openjdk-17-crac+3_linux-x64 ENV PATH=$PATH:/opt/openjdk-17-crac+3_linux-x64/bin # # Add checkpoint directory. # ADD cr cr # # Restore java process from checkpoint. # CMD ["java", "-XX:CRaCRestoreFrom=cr"] ================================================ FILE: http/crac/README.md ================================================ ## CRaC integration Make sure you are using a CRaC enabled JDK available from https://github.com/CRaC/openjdk-builds/releases To create the checkpoint: 1. java -XX:CRaCCheckpointTo=cr -jar piranha-dist-servlet.jar --enable-crac --write-pid --war-file your.war & 2. jcmd piranha-dist-servlet.jar JDK.checkpoint 3. files should be in the cr directory To restore from checkpoint: 1. java -XX:CRaCRestoreFrom=cr ================================================ FILE: http/crac/pom.xml ================================================ 4.0.0 cloud.piranha.http project 25.4.0-SNAPSHOT piranha-http-crac Piranha - HTTP - CRaC integration cloud.piranha.http piranha-http-api ${project.version} io.github.crac org-crac compile ================================================ FILE: http/crac/src/main/java/cloud/piranha/http/crac/CracHttpServer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.crac; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.api.HttpServerProcessor; import java.lang.System.Logger; import org.crac.Context; import org.crac.Core; import org.crac.Resource; /** * The CRaC integration for the HttpServer API. */ public class CracHttpServer implements HttpServer, Resource { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(CracHttpServer.class.getName()); /** * Stores the delegate. */ private HttpServer delegate; /** * Stores the checkpoint flag. */ private boolean checkpointing; /** * Constructor. * * @param delegate the delegate. */ public CracHttpServer(HttpServer delegate) { this.delegate = delegate; Core.getGlobalContext().register(this); } @Override public void afterRestore(Context context) throws Exception { delegate.start(); checkpointing = false; } @Override public void beforeCheckpoint(Context context) throws Exception { checkpointing = true; LOGGER.log(Logger.Level.INFO, "Stopping HTTP server"); delegate.stop(); LOGGER.log(Logger.Level.INFO, "Stopped HTTP server"); LOGGER.log(Logger.Level.INFO, "Giving sockets time to close"); try { Thread.sleep(30000); } catch(InterruptedException ie) { Thread.currentThread().interrupt(); } LOGGER.log(Logger.Level.INFO, "Sockets should be closed"); } @Override public HttpServerProcessor getHttpServerProcessor() { return delegate.getHttpServerProcessor(); } @Override public boolean getSSL() { return delegate.getSSL(); } @Override public int getServerPort() { return delegate.getServerPort(); } @Override public boolean isRunning() { boolean running = true; if (!checkpointing) { running = delegate.isRunning(); } return running; } @Override public void setHttpServerProcessor(HttpServerProcessor httpServerProcessor) { delegate.setHttpServerProcessor(httpServerProcessor); } @Override public void setSSL(boolean ssl) { delegate.setSSL(ssl); } @Override public void setServerPort(int serverPort) { delegate.setServerPort(serverPort); } @Override public void start() { delegate.start(); } @Override public void stop() { delegate.stop(); } } ================================================ FILE: http/crac/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the CRaC integration for the HTTP engine. * *

* This module integrates Project CRaC into Piranha. See * https://wiki.openjdk.org/display/crac/Main for more information about its * project. *

* * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.http.crac { exports cloud.piranha.http.crac; opens cloud.piranha.http.crac; requires transitive cloud.piranha.http.api; requires transitive org.crac; } ================================================ FILE: http/grizzly/pom.xml ================================================ 4.0.0 cloud.piranha.http project 25.4.0-SNAPSHOT piranha-http-grizzly jar Piranha - HTTP - Grizzly Integration cloud.piranha.http piranha-http-api ${project.version} compile org.glassfish.grizzly grizzly-http-server compile org.glassfish.grizzly grizzly-http2 compile org.glassfish.grizzly grizzly-npn-api compile cloud.piranha.http piranha-http-tests ${project.version} test ================================================ FILE: http/grizzly/src/main/java/cloud/piranha/http/grizzly/GrizzlyHttpServer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.grizzly; import cloud.piranha.http.api.HttpServerProcessor; import cloud.piranha.http.api.HttpServerProcessorEndState; import static cloud.piranha.http.api.HttpServerProcessorEndState.ASYNCED; import java.io.IOException; import java.util.concurrent.Semaphore; import static java.util.concurrent.TimeUnit.SECONDS; import static java.lang.System.Logger.Level.WARNING; import java.lang.System.Logger; import org.glassfish.grizzly.CompletionHandler; import org.glassfish.grizzly.http.server.HttpHandler; import org.glassfish.grizzly.http.server.HttpServer; import org.glassfish.grizzly.http.server.NetworkListener; import org.glassfish.grizzly.http.server.Request; import org.glassfish.grizzly.http.server.Response; import org.glassfish.grizzly.http2.Http2AddOn; /** * The Grizzly implementation of HTTP Server. * * @author Manfred Riem (mriem@manorrock.com) * @see cloud.piranha.http.api.HttpServer */ public class GrizzlyHttpServer implements cloud.piranha.http.api.HttpServer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(GrizzlyHttpServer.class.getName()); /** * Stores the Grizzly HttpServer. */ private HttpServer httpServer; /** * Stores the HTTP server processor. */ private HttpServerProcessor httpServerProcessor; /** * * * Stores the SSL flag */ private boolean ssl; /** * * * Stores the server port */ private int port; /** * Constructor. */ public GrizzlyHttpServer() { } /** * Constructor * * @param serverPort the server port. */ public GrizzlyHttpServer(int serverPort) { port = serverPort; } /** * Constructor * * @param serverPort the server port. * @param httpServerProcessor the HTTP server processor; */ public GrizzlyHttpServer(int serverPort, HttpServerProcessor httpServerProcessor) { port = serverPort; this.httpServerProcessor = httpServerProcessor; } /** * Constructor * * @param httpServer the Grizzly HTTP server. */ public GrizzlyHttpServer(HttpServer httpServer) { this.httpServer = httpServer; } @Override public HttpServerProcessor getHttpServerProcessor() { return httpServerProcessor; } @Override public int getServerPort() { return port; } @Override public boolean getSSL() { return ssl; } @Override public boolean isRunning() { return httpServer != null; } @Override public void setHttpServerProcessor(HttpServerProcessor httpServerProcessor) { this.httpServerProcessor = httpServerProcessor; } @Override public void setServerPort(int serverPort) { this.port = serverPort; } @Override public void setSSL(boolean ssl) { this.ssl = ssl; } @Override public void start() { if (httpServer == null) { httpServer = HttpServer.createSimpleServer(null, port); NetworkListener networkListener = httpServer.getListener("grizzly"); networkListener.setSecure(ssl); networkListener.registerAddOn(new Http2AddOn()); } httpServer.getServerConfiguration().setPassTraceRequest(true); httpServer.getServerConfiguration().addHttpHandler(new HttpHandler() { @Override public void service(Request request, Response response) throws Exception { GrizzlyHttpServerRequest gRequest = new GrizzlyHttpServerRequest(request); GrizzlyHttpServerResponse gResponse = new GrizzlyHttpServerResponse(response); HttpServerProcessorEndState state = httpServerProcessor.process(gRequest, gResponse); if (state == ASYNCED) { response.suspend(60, SECONDS, new CompletionHandler() { @Override public void cancelled() { } @Override public void failed(Throwable thrwbl) { } @Override public void completed(Response e) { } @Override public void updated(Response e) { } }); } } }); try { httpServer.start(); } catch (IOException ioe) { LOGGER.log(WARNING, "An I/O error occurred while starting the HTTP server", ioe); } } @Override public void stop() { Semaphore lock = new Semaphore(1); lock.acquireUninterruptibly(); httpServer.shutdown(5, SECONDS).addCompletionHandler( new CompletionHandler() { @Override public void cancelled() { lock.release(); } @Override public void failed(Throwable thrwbl) { lock.release(); } @Override public void completed(HttpServer e) { lock.release(); } @Override public void updated(HttpServer e) { lock.release(); } }); try { lock.acquire(); } catch (InterruptedException ie) { LOGGER.log(WARNING, "Interrupted while waiting for the HTTP server to shut down", ie); } httpServer = null; } } ================================================ FILE: http/grizzly/src/main/java/cloud/piranha/http/grizzly/GrizzlyHttpServerRequest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.grizzly; import cloud.piranha.http.api.HttpServerRequest; import java.io.InputStream; import java.util.Iterator; import org.glassfish.grizzly.http.server.Request; /** * The Grizzly implementation of HttpServerRequest. * * @author Manfred Riem (mriem@manorrock.com) * @see HttpServerRequest */ public class GrizzlyHttpServerRequest implements HttpServerRequest { /** * Stores the Grizzly request. */ private final Request request; /** * Constructor. * * @param request the Grizzly request. */ public GrizzlyHttpServerRequest(Request request) { this.request = request; } @Override public String getHeader(String name) { return request.getHeader(name); } @Override public Iterator getHeaderNames() { return request.getHeaderNames().iterator(); } @Override public Iterator getHeaders(String name) { return request.getHeaders(name).iterator(); } @Override public InputStream getInputStream() { return request.getInputStream(); } @Override public String getLocalAddress() { return request.getLocalAddr(); } @Override public String getLocalHostname() { return request.getLocalName(); } @Override public int getLocalPort() { return request.getLocalPort(); } @Override public String getMethod() { return request.getMethod().getMethodString(); } @Override public String getRemoteAddress() { return request.getRemoteAddr(); } @Override public String getRemoteHostname() { return request.getRemoteHost(); } @Override public int getRemotePort() { return request.getRemotePort(); } @Override public String getRequestTarget() { String requestTarget; if (request.getQueryString() != null) { requestTarget = request.getRequestURI() + "?" + request.getQueryString(); } else { requestTarget = request.getRequestURI(); } return requestTarget; } @Override public String getProtocol() { return request.getProtocol().getProtocolString(); } @Override public boolean isSecure() { return request.isSecure(); } } ================================================ FILE: http/grizzly/src/main/java/cloud/piranha/http/grizzly/GrizzlyHttpServerResponse.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.grizzly; import java.io.IOException; import java.io.OutputStream; import org.glassfish.grizzly.http.server.Response; import cloud.piranha.http.api.HttpServerResponse; /** * The Grizzly implementation of HTTP Server Response. * * @author Manfred Riem (mriem@manorrock.com) */ public class GrizzlyHttpServerResponse implements HttpServerResponse { /** * Stores the response. */ private final Response response; /** * Constructor. * * @param response the Grizzly response. */ public GrizzlyHttpServerResponse(Response response) { this.response = response; } @Override public void addHeader(String name, String value) { response.addHeader(name, value); } @Override public String getHeader(String name) { return response.getHeader(name); } @Override public OutputStream getOutputStream() { return response.getOutputStream(); } @Override public void setHeader(String name, String value) { response.setHeader(name, value); } @Override public void setStatus(int status) { response.setStatus(status); } @Override public void writeHeaders() throws IOException { // writing the headers is taken care of when writing out the response. } @Override public void writeStatusLine() throws IOException { // writing the status line is taken care of when writing out the response. } } ================================================ FILE: http/grizzly/src/main/java/cloud/piranha/http/grizzly/package-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** *

* This package delivers a Grizzly implementation of the HTTP Server API. *

* * @author Manfred Riem (mriem@manorrock.com) */ package cloud.piranha.http.grizzly; ================================================ FILE: http/grizzly/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Grizzly implementation of the HTTP engine. * *

* This module integrates Eclipse Grizzly into Piranha. See * https://github.com/eclipse-ee4j/grizzly for more information about its * project. *

* * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.http.grizzly { exports cloud.piranha.http.grizzly; opens cloud.piranha.http.grizzly; requires transitive cloud.piranha.http.api; requires org.glassfish.grizzly; requires transitive org.glassfish.grizzly.http; requires transitive org.glassfish.grizzly.http.server; requires org.glassfish.grizzly.http2; } ================================================ FILE: http/grizzly/src/test/java/cloud/piranha/http/grizzly/GrizzlyHttpServerRequestTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.grizzly; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.api.HttpServerProcessor; import cloud.piranha.http.tests.HttpServerRequestTest; /** * The JUnit tests for the DefaultHttpServer class. * * @author Manfred Riem (mriem@manorrock.com) */ public class GrizzlyHttpServerRequestTest extends HttpServerRequestTest { /** * Create the Netty HTTP server. * * @param portNumber the port number. * @param processor the HTTP server processor. * @return the Netty HTTP server. */ @Override protected HttpServer createServer(int portNumber, HttpServerProcessor processor) { return new GrizzlyHttpServer(portNumber, processor); } } ================================================ FILE: http/grizzly/src/test/java/cloud/piranha/http/grizzly/GrizzlyHttpServerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.grizzly; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.api.HttpServerProcessor; import cloud.piranha.http.tests.HttpServerTest; import cloud.piranha.http.tests.TestHttpServerProcessor; /** * The JUnit tests for the DefaultHttpServer class. * * @author Manfred Riem (mriem@manorrock.com) */ public class GrizzlyHttpServerTest extends HttpServerTest { /** * Create the Netty HTTP server. * * @param portNumber the port number. * @return the Netty HTTP server. */ @Override protected HttpServer createServer(int portNumber) { GrizzlyHttpServer server = new GrizzlyHttpServer(portNumber); server.setHttpServerProcessor(new TestHttpServerProcessor()); return server; } /** * Create the Netty HTTP server. * * @param portNumber the port number. * @param processor the HTTP server processor. * @return the Netty HTTP server. */ @Override protected HttpServer createServer(int portNumber, HttpServerProcessor processor) { return new GrizzlyHttpServer(portNumber, processor); } } ================================================ FILE: http/impl/pom.xml ================================================ 4.0.0 cloud.piranha.http project 25.4.0-SNAPSHOT piranha-http-impl jar Piranha - HTTP - Implementation cloud.piranha.http piranha-http-api ${project.version} compile cloud.piranha.http piranha-http-tests ${project.version} test org.apache.maven.plugins maven-compiler-plugin --add-modules=java.net.http org.apache.maven.plugins maven-surefire-plugin --add-opens cloud.piranha.http.impl/cloud.piranha.http.impl=ALL-UNNAMED --add-reads cloud.piranha.http.impl=java.net.http ================================================ FILE: http/impl/src/main/java/cloud/piranha/http/impl/DefaultHttpServer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.impl; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.api.HttpServerProcessor; import java.io.IOException; import java.lang.System.Logger; import static java.lang.System.Logger.Level.DEBUG; import static java.lang.System.Logger.Level.WARNING; import java.net.ServerSocket; import java.net.Socket; import java.security.NoSuchAlgorithmException; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; /** * The default implementation of a HTTP Server. * *

* When you create the DefaultHttpServer with a port number of -2, it will * automatically assign a random port in the range of 1025-9999 when you call * start(). You can determine which port was assigned by calling * getServerPort(). *

* * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultHttpServer implements HttpServer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(DefaultHttpServer.class.getName()); /** * Stores the executor service. */ protected ExecutorService executorService; /** * Stores the processor. */ protected HttpServerProcessor processor; /** * Stores the port we are listening on. */ protected int serverPort; /** * Stores the server acceptor thread. */ protected Thread serverAcceptorThread; /** * Stores the server socket. */ protected ServerSocket serverSocket; /** * Stores the server stop request. */ protected boolean serverStopRequest; /** * Stores the SO_TIMEOUT. */ protected int soTimeout = 60000; /** * Stores the SSL flag. */ protected boolean ssl; /** * Stores the thread factory. */ protected ThreadFactory threadFactory; /** * Constructor */ public DefaultHttpServer() { threadFactory = new DefaultHttpServerThreadFactory(); serverPort = -2; serverStopRequest = false; } /** * Constructor. * * @param serverPort the server port. */ public DefaultHttpServer(int serverPort) { this.serverPort = serverPort; threadFactory = new DefaultHttpServerThreadFactory(); } /** * Constructor * * @param serverPort the server port. * @param processor the HTTP server processor. * @param ssl the SSL flag. */ public DefaultHttpServer(int serverPort, HttpServerProcessor processor, boolean ssl) { this.threadFactory = new DefaultHttpServerThreadFactory(); this.processor = processor; this.serverPort = serverPort; this.serverStopRequest = false; this.ssl = ssl; } /** * Constructor * * @param serverPort the server port. * @param processor the HTTP server processor. * @param soTimeout the SO_TIMEOUT. */ public DefaultHttpServer(int serverPort, HttpServerProcessor processor, int soTimeout) { this.threadFactory = new DefaultHttpServerThreadFactory(); this.processor = processor; this.serverPort = serverPort; this.serverStopRequest = false; this.soTimeout = soTimeout; } @Override public HttpServerProcessor getHttpServerProcessor() { return processor; } @Override public int getServerPort() { return serverPort; } /** * {@return the SO_TIMEOUT} */ public int getSoTimeout() { return soTimeout; } @Override public boolean getSSL() { return ssl; } @Override public boolean isRunning() { boolean result = false; if (executorService != null) { result = !executorService.isShutdown(); } return result; } @Override public void setHttpServerProcessor(HttpServerProcessor httpServerProcessor) { processor = httpServerProcessor; } @Override public void setServerPort(int serverPort) { this.serverPort = serverPort; } @Override public void setSSL(boolean ssl) { this.ssl = ssl; } @Override public void start() { LOGGER.log(DEBUG, () -> "Starting HTTP server on port " + serverPort); try { determineServerPort(); executorService = Executors.newCachedThreadPool(threadFactory); serverStopRequest = false; if (ssl) { SSLContext context = SSLContext.getDefault(); SSLEngine engine = context.createSSLEngine(); SSLServerSocketFactory factory = context.getServerSocketFactory(); SSLServerSocket socket = (SSLServerSocket) factory.createServerSocket(serverPort); SSLParameters parameters = new SSLParameters(); parameters.setCipherSuites(engine.getSupportedCipherSuites()); parameters.setProtocols(engine.getSupportedProtocols()); parameters.setNeedClientAuth(false); parameters.setWantClientAuth(true); socket.setSSLParameters(parameters); serverSocket = socket; } else { serverSocket = new ServerSocket(serverPort); } serverSocket.setReuseAddress(true); serverSocket.setSoTimeout(soTimeout); serverAcceptorThread = new Thread(new DefaultHttpServerAcceptorThread(this), "DefaultHttpServer-AcceptorThread"); serverAcceptorThread.start(); LOGGER.log(DEBUG, () -> "Started HTTP server on port " + serverPort); } catch (IOException exception) { LOGGER.log(WARNING, "An I/O error occurred while starting the HTTP server", exception); } catch (NoSuchAlgorithmException ex) { LOGGER.log(WARNING, "Unable to match SSL algorithm", ex); } } @Override public void stop() { LOGGER.log(DEBUG, () -> "Stopping HTTP server on port " + serverPort); serverStopRequest = true; if (serverSocket != null) { try { serverSocket.close(); } catch (IOException exception) { LOGGER.log(WARNING, "An I/O error occurred while stopping the HTTP server", exception); } } if (executorService != null) { executorService.shutdown(); try { executorService.awaitTermination(120, TimeUnit.SECONDS); } catch (InterruptedException exception) { LOGGER.log(WARNING, "Termination of the executor service was interrupted", exception); Thread.currentThread().interrupt(); } } LOGGER.log(DEBUG, () -> "Stopped HTTP server on port " + serverPort); } private void determineServerPort() { if (serverPort == -2) { Random random = new Random(); while (serverPort == -2) { int candidatePort = random.nextInt(1025, 9999); try (Socket socket = new Socket("127.0.0.1", candidatePort)) { socket.getInputStream(); } catch (IOException ex) { serverPort = candidatePort; break; } } } } } ================================================ FILE: http/impl/src/main/java/cloud/piranha/http/impl/DefaultHttpServerAcceptorThread.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.impl; import static java.lang.System.Logger.Level.WARNING; import java.io.IOException; import java.net.Socket; import java.lang.System.Logger; /** * The acceptor thread used by the default implementation of HTTP server. * *

* This thread is waiting for socket connections, and once it gets a socket * connection it hands it off for processing to a processing thread. *

* * @author Manfred Riem (mriem@manorrock.com) */ class DefaultHttpServerAcceptorThread implements Runnable { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger( DefaultHttpServerAcceptorThread.class.getName()); /** * Stores the HTTP server. */ private final DefaultHttpServer server; /** * Constructor. * * @param server the server we are working for. */ public DefaultHttpServerAcceptorThread(DefaultHttpServer server) { this.server = server; } /** * @see Runnable#run() */ @Override public void run() { while (!server.serverStopRequest) { try { Socket socket = server.serverSocket.accept(); socket.setSoTimeout(60000); server.executorService.execute(new DefaultHttpServerProcessingThread(server, socket)); } catch (IOException exception) { // not interesting to do anything with this here as the client probably just hung up. } catch (Throwable throwable) { LOGGER.log(WARNING, "An error occurred while accepting a socket connection", throwable); } } } } ================================================ FILE: http/impl/src/main/java/cloud/piranha/http/impl/DefaultHttpServerProcessingThread.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.impl; import cloud.piranha.http.api.HttpServerProcessorEndState; import static cloud.piranha.http.api.HttpServerProcessorEndState.COMPLETED; import static java.lang.System.Logger.Level.WARNING; import java.io.IOException; import java.net.Socket; import java.lang.System.Logger; /** * A processing thread used by the default implementation of HTTP server. * *

* This thread is used to process a HTTP Server Request and generate a HTTP * Server Response. It does so by giving it to a HTTP Server Processor. *

* * @author Manfred Riem (mriem@manorrock.com) */ class DefaultHttpServerProcessingThread implements Runnable { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger( DefaultHttpServerProcessingThread.class.getName()); /** * Stores the server. */ private final DefaultHttpServer server; /** * Stores the socket. */ private final Socket socket; /** * Constructor. * * @param server the server we are working for. * @param socket the socket we are dealing with. */ public DefaultHttpServerProcessingThread(DefaultHttpServer server, Socket socket) { this.server = server; this.socket = socket; } /** * @see Runnable#run() */ @Override public void run() { HttpServerProcessorEndState state = COMPLETED; DefaultHttpServerResponse response = null; try { DefaultHttpServerRequest request = new DefaultHttpServerRequest(socket); response = new DefaultHttpServerResponse(socket); state = server.processor.process(request, response); } finally { if (state == COMPLETED) { try { socket.shutdownInput(); // Give the client a chance to start reading the stream. // If we disconnect right away the client may get an // Unexpected Exception: java.net.SocketException: Connection reset // // If we don't disconnect the client may hang. try { Thread.sleep(5); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } if (response != null) { response.closeResponse(); } else { socket.close(); } } catch (IOException exception) { LOGGER.log(WARNING, "An I/O error occurred during processing of the request", exception); } } } } } ================================================ FILE: http/impl/src/main/java/cloud/piranha/http/impl/DefaultHttpServerProcessor.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.impl; import cloud.piranha.http.api.HttpServerProcessor; import cloud.piranha.http.api.HttpServerProcessorEndState; import static cloud.piranha.http.api.HttpServerProcessorEndState.COMPLETED; import cloud.piranha.http.api.HttpServerRequest; import cloud.piranha.http.api.HttpServerResponse; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.lang.System.Logger; import static java.lang.System.Logger.Level.ERROR; /** * The default implementation of a HTTP Server Processor. * *

* This HTTP Server Processor will either show the user with a directory * listing, or stream back contents of the file clicked on in the browser, or it * will return a 404 error because neither could be found. *

* * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultHttpServerProcessor implements HttpServerProcessor { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger( DefaultHttpServerProcessor.class.getName()); /** * Stores the error writing response message. */ private static final String IO_ERROR_WRITING_RESPONSE = "An I/O error occurred while writing the response"; /** * Constructor. */ public DefaultHttpServerProcessor() { } @Override public HttpServerProcessorEndState process(HttpServerRequest request, HttpServerResponse response) { response.setStatus(200); File baseDir = new File(System.getProperty("user.dir")); File file = new File(baseDir, request.getRequestTarget()); if (file.exists() && file.isDirectory() && "GET".equals(request.getMethod())) { response.setHeader("Content-Type", "text/html"); try { response.writeStatusLine(); response.writeHeaders(); OutputStream outputStream = response.getOutputStream(); PrintWriter writer = new PrintWriter(outputStream); writer.println(""); writer.println(file.getName() + "
"); writer.print(".
"); writer.print("..
"); File[] fileList = file.listFiles(); if (fileList != null && fileList.length > 0) { for (int i = 0; i < fileList.length; i++) { String uri = fileList[i].getAbsolutePath().substring(baseDir.getAbsolutePath().length()); if (uri.startsWith("//")) { uri = uri.substring(1); } writer.println("" + fileList[i].getName() + "
" ); } } writer.println(""); writer.flush(); } catch (IOException exception) { LOGGER.log(ERROR, IO_ERROR_WRITING_RESPONSE, exception); } } else if (file.exists() && !file.isDirectory()) { response.setHeader("Content-Type", "application/octet-stream"); response.setHeader("Content-Length", Long.toString(file.length())); try { response.writeStatusLine(); response.writeHeaders(); OutputStream outputStream = response.getOutputStream(); try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) { int read = inputStream.read(); while (read != -1) { outputStream.write((char) read); read = inputStream.read(); } outputStream.flush(); } } catch (IOException exception) { LOGGER.log(ERROR, IO_ERROR_WRITING_RESPONSE, exception); } } else { try { response.setStatus(404); response.writeStatusLine(); response.writeHeaders(); } catch (IOException exception) { LOGGER.log(ERROR, IO_ERROR_WRITING_RESPONSE, exception); } } return COMPLETED; } } ================================================ FILE: http/impl/src/main/java/cloud/piranha/http/impl/DefaultHttpServerRequest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.impl; import cloud.piranha.http.api.HttpServerRequest; import java.io.IOException; import java.io.InputStream; import java.net.Socket; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import static java.lang.System.Logger.Level.WARNING; import java.lang.System.Logger; import java.security.Principal; import java.security.PublicKey; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; /** * The default implementation of HttpServerRequest. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultHttpServerRequest implements HttpServerRequest { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger( DefaultHttpServerRequest.class.getName()); /** * Stores the headers. */ private final Map> headers; /** * Stores the input stream. */ private InputStream inputStream; /** * Stores the method. */ private String method; /** * Stores the protocol */ private String protocol; /** * Stores the request target. */ private String requestTarget; /** * Stores the SSL certificates. */ private X509Certificate[] sslCertificates; /** * Stores the SSL cipher suite. */ private String sslCipherSuite; /** * Stores the SSL key size. */ private int sslKeySize; /** * Stores the SSL principal. */ private Principal sslPrincipal; /** * Stores the socket. */ private final Socket socket; /** * Constructor. * * @param socket the socket. */ public DefaultHttpServerRequest(Socket socket) { this.headers = new HashMap<>(1); this.socket = socket; protocol = "HTTP/1.1"; parse(); } /** * Add the header. * * @param name the name. * @param value the value. */ public void addHeader(String name, String value) { if (!headers.containsKey(name.toUpperCase())) { ArrayList values = new ArrayList<>(); values.add(value); headers.put(name.toUpperCase(), values); } else { headers.get(name.toUpperCase()).add(value); } } @Override public String getHeader(String name) { String header = null; if (headers.get(name.toUpperCase()) != null) { header = headers.get(name.toUpperCase()).isEmpty() ? null : headers.get(name.toUpperCase()).get(0); } return header; } @Override public Iterator getHeaderNames() { return headers.keySet().iterator(); } @Override public Iterator getHeaders(String name) { return headers.get(name.toUpperCase()) == null ? new ArrayList().iterator() : headers.get(name.toUpperCase()).iterator(); } @Override public InputStream getInputStream() { InputStream result = inputStream; if (inputStream == null) { try { inputStream = socket.getInputStream(); result = inputStream; } catch (IOException exception) { LOGGER.log(WARNING, "An I/O error occurred while acquiring input stream", exception); } } return result; } @Override public String getLocalAddress() { return socket.getLocalAddress().getHostAddress(); } @Override public String getLocalHostname() { return socket.getLocalAddress().getHostName(); } @Override public int getLocalPort() { return socket.getLocalPort(); } @Override public String getMethod() { return method; } @Override public String getProtocol() { return protocol; } @Override public String getRemoteAddress() { return socket.getInetAddress().getHostAddress(); } @Override public String getRemoteHostname() { return socket.getInetAddress().getHostName(); } @Override public int getRemotePort() { return socket.getPort(); } @Override public String getRequestTarget() { return requestTarget; } @Override public X509Certificate[] getSslCertificates() { return sslCertificates; } @Override public String getSslCipherSuite() { return sslCipherSuite; } @Override public int getSslKeySize() { return sslKeySize; } @Override public Principal getSslPrincipal() { return sslPrincipal; } @Override public boolean isSecure() { return socket instanceof SSLSocket; } /** * Parse the request. * */ private void parse() { if (socket != null) { try { InputStream parseStream = socket.getInputStream(); StringBuilder line = new StringBuilder(); int read = parseStream.read(); boolean requestLineParsed = false; if (read != -1) { while (read != -1 && parseStream.available() > 0) { if ('\r' != (char) read) { line.append((char) read); } read = parseStream.read(); if ('\n' == (char) read) { if (line.length() > 0) { if (!requestLineParsed) { parseRequestLine(line.toString()); requestLineParsed = true; } else { parseHeader(line.toString()); } line = new StringBuilder(); read = parseStream.read(); } else { read = -1; } } } } if (isSecure()) { SSLSocket sslSocket = (SSLSocket) socket; SSLSession sslSession = sslSocket.getSession(); if (sslSession.getCipherSuite() != null) { setSslCipherSuite(sslSession.getCipherSuite()); } try { Certificate[] certificates = sslSession.getPeerCertificates(); if (certificates != null && certificates.length > 0) { if (certificates[0] instanceof X509Certificate x509) { PublicKey publicKey = x509.getPublicKey(); setSslKeySize(publicKey.getEncoded().length * 8); } ArrayList x509Certificates = new ArrayList<>(); for (Certificate certificate : certificates) { if (certificate instanceof X509Certificate x509Certificate) { x509Certificates.add(x509Certificate); } } setSslCertificates(x509Certificates.toArray(X509Certificate[]::new)); setSslPrincipal(sslSession.getPeerPrincipal()); } } catch (SSLPeerUnverifiedException e) { } } } catch (Exception exception) { LOGGER.log(WARNING, "An exception occurred while parsing the request", exception); } } } /** * Parse the header. * * @param line the header line. */ private void parseHeader(String line) { String name = line.substring(0, line.indexOf(':')).trim(); String value = line.substring(line.indexOf(':') + 1).trim(); addHeader(name, value); } /** * Parse the request line. * * @param line the request line. */ private void parseRequestLine(String line) { int index = line.indexOf(' '); setMethod(line.substring(0, index)); line = line.substring(index + 1); index = line.indexOf(' '); requestTarget = line.substring(0, index); String protocolRequestLine = line.substring(index + 1); if (!protocol.isEmpty()) { protocol = protocolRequestLine; } } /** * Set the method. * * @param method the method. */ public void setMethod(String method) { this.method = method; } /** * Set the request target. * * @param requestTarget the request target. */ public void setRequestTarget(String requestTarget) { this.requestTarget = requestTarget; } /** * Set the SSL certificates. * * @param sslCertificates the SSL certificates. */ public void setSslCertificates(X509Certificate[] sslCertificates) { this.sslCertificates = sslCertificates; } /** * Set the SSL cipher suite. * * @param sslCipherSuite the cipher suite. */ public void setSslCipherSuite(String sslCipherSuite) { this.sslCipherSuite = sslCipherSuite; } /** * Set the SSL key size. * * @param sslKeySize the SSL key size. */ public void setSslKeySize(int sslKeySize) { this.sslKeySize = sslKeySize; } /** * Set the SSL principal. * * @param sslPrincipal the SSL principal. */ public void setSslPrincipal(Principal sslPrincipal) { this.sslPrincipal = sslPrincipal; } } ================================================ FILE: http/impl/src/main/java/cloud/piranha/http/impl/DefaultHttpServerResponse.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.impl; import static java.lang.System.Logger.Level.WARNING; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.util.Iterator; import java.util.Map; import java.lang.System.Logger; import cloud.piranha.http.api.HttpServerResponse; import java.util.ArrayList; import java.util.HashMap; import java.util.List; /** * The default implementation of HTTP Server Response. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultHttpServerResponse implements HttpServerResponse { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger( DefaultHttpServerResponse.class.getName()); /** * Stores the headers. */ private final Map> headers; /** * Stores the output stream. */ private OutputStream outputStream; /** * Stores the socket. */ private final Socket socket; /** * Stores the status. */ private int status; /** * Constructor. * * @param socket the socket. */ public DefaultHttpServerResponse(Socket socket) { this.headers = new HashMap<>(1); this.socket = socket; } @Override public void addHeader(String name, String value) { List values = headers.get(name); if (values == null) { synchronized (this) { values = new ArrayList<>(); headers.put(name, values); } } values.add(value != null ? value : ""); } @Override public void closeResponse() throws IOException { HttpServerResponse.super.closeResponse(); socket.close(); } @Override public String getHeader(String name) { return headers.get(name) == null ? null : headers.get(name).get(0); } @Override public OutputStream getOutputStream() { if (outputStream == null) { try { outputStream = socket.getOutputStream(); } catch (IOException exception) { LOGGER.log(WARNING, () -> "An I/O error occurred while acquiring the output stream", exception); } } return outputStream; } @Override public void setHeader(String name, String value) { ArrayList values = new ArrayList<>(); values.add(value != null ? value : ""); headers.put(name, values); } @Override public void setStatus(int status) { this.status = status; } /** * Write the header. * * @param name the header name. * @throws IOException when an I/O error occurs. */ private void writeHeader(String name) throws IOException { OutputStream output = getOutputStream(); output.write(name.getBytes()); output.write(": ".getBytes()); Iterator values = headers.get(name).iterator(); while (values.hasNext()) { output.write(values.next().getBytes()); if (values.hasNext()) { output.write(", ".getBytes()); } } output.write("\n".getBytes()); } @Override public void writeHeaders() throws IOException { for (String name : headers.keySet()) { writeHeader(name); } OutputStream output = getOutputStream(); output.write("\n".getBytes()); } @Override public void writeStatusLine() throws IOException { OutputStream output = getOutputStream(); output.write("HTTP/1.1".getBytes()); output.write(" ".getBytes()); output.write(Integer.toString(status).getBytes()); output.write("\n".getBytes()); } } ================================================ FILE: http/impl/src/main/java/cloud/piranha/http/impl/DefaultHttpServerThreadFactory.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.impl; import static java.lang.System.Logger.Level.DEBUG; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; import java.lang.System.Logger; /** * The thread factory used by the default implementation of HTTP server. * *

* This thread factory is when vending new processing threads. *

* * @author Manfred Riem (mriem@manorrock.com) */ class DefaultHttpServerThreadFactory implements ThreadFactory { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger( DefaultHttpServerThreadFactory.class.getName()); /** * Stores the id. */ private final AtomicInteger id = new AtomicInteger(0); /** * @see ThreadFactory#newThread(java.lang.Runnable) */ @Override public Thread newThread(Runnable runnable) { LOGGER.log(DEBUG, "Creating new processing thread"); return new Thread(runnable, "DefaultHttpServer-ProcessingThread-" + id.getAndIncrement()); } } ================================================ FILE: http/impl/src/main/java/cloud/piranha/http/impl/package-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** *

* This package delivers the default implementation of the HTTP Server API. *

* * @author Manfred Riem (mriem@manorrock.com) */ package cloud.piranha.http.impl; ================================================ FILE: http/impl/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the default implementation of the HTTP engine. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.http.impl { exports cloud.piranha.http.impl; opens cloud.piranha.http.impl; requires transitive cloud.piranha.http.api; } ================================================ FILE: http/impl/src/test/java/cloud/piranha/http/impl/DefaultHttpServerRequestTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.impl; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * The JUnit tests for the DefaultHttpServerRequest class. * * @author Manfred Riem (mriem@manorrock.com) */ class DefaultHttpServerRequestTest { /** * Test getHeader method. * * @throws Exception when a serious error occurs. */ @Test void testGetHeader() throws Exception { DefaultHttpServerRequest request = new DefaultHttpServerRequest(null); request.addHeader("Name", "value"); assertEquals("value", request.getHeader("Name")); assertEquals(request.getHeader("NAME"), request.getHeader("name")); } } ================================================ FILE: http/impl/src/test/java/cloud/piranha/http/impl/DefaultHttpServerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.impl; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.api.HttpServerProcessor; import cloud.piranha.http.tests.HttpServerTest; import cloud.piranha.http.tests.TestHttpServerProcessor; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; /** * The JUnit tests for the DefaultHttpServer class. * * @author Manfred Riem (mriem@manorrock.com) */ class DefaultHttpServerTest extends HttpServerTest { /** * Create the server. * * @param portNumber the port number. * @return the HTTP server. */ @Override protected HttpServer createServer(int portNumber) { return new DefaultHttpServer(portNumber, new TestHttpServerProcessor(), false); } /** * Create the server. * * @param portNumber the port number. * @param processor the HTTP server processor. * @return the HTTP server. */ @Override protected HttpServer createServer(int portNumber, HttpServerProcessor processor) { return new DefaultHttpServer(portNumber, processor, false); } /** * Test SO_TIMEOUT. */ @Test void testSoTimeout() { int portOffset = 0; try { portOffset = SecureRandom.getInstanceStrong().nextInt(1000); } catch (NoSuchAlgorithmException e) { // TODO Auto-generated catch block e.printStackTrace(); } int port = 8760 + portOffset; DefaultHttpServer server = new DefaultHttpServer( port, new TestHttpServerProcessor(), 2000); assertEquals(2000, server.getSoTimeout()); server.start(); try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder(URI.create("http://localhost:" + port + "/")).build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.discarding()); assertEquals(200, response.statusCode()); } catch (IOException | InterruptedException ioe) { throw new RuntimeException(ioe); } finally { server.stop(); } } } ================================================ FILE: http/jdk/pom.xml ================================================ 4.0.0 cloud.piranha.http project 25.4.0-SNAPSHOT piranha-http-jdk jar Piranha - HTTP - JDK HTTP Server Integration cloud.piranha.http piranha-http-api ${project.version} compile cloud.piranha.http piranha-http-tests ${project.version} test ================================================ FILE: http/jdk/src/main/java/cloud/piranha/http/jdk/JdkHttpHandler.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.jdk; import cloud.piranha.http.api.HttpServerProcessor; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import java.io.IOException; /** * The HttpHandler used by the JDK HttpServer implementation of HttpServer. * * @author Manfred Riem (mriem@manorrock.com) */ public class JdkHttpHandler implements HttpHandler { /** * Stores the HTTP server processor. */ private final HttpServerProcessor httpServerProcessor; /** * Constructor. * * @param httpServerProcessor the HTTP server processor. */ public JdkHttpHandler(HttpServerProcessor httpServerProcessor) { this.httpServerProcessor = httpServerProcessor; } @Override public void handle(HttpExchange exchange) throws IOException { try (exchange) { httpServerProcessor.process( new JdkHttpRequest(exchange), new JdkHttpResponse(exchange)); } } } ================================================ FILE: http/jdk/src/main/java/cloud/piranha/http/jdk/JdkHttpRequest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.jdk; import cloud.piranha.http.api.HttpServerRequest; import com.sun.net.httpserver.HttpExchange; import java.io.InputStream; import java.util.Iterator; /** * The JDK HttpServer version of a HttpServerRequest. * * @author Manfred Riem (mriem@manorrock.com) */ public class JdkHttpRequest implements HttpServerRequest { /** * Stores the HTTP exchange. */ private final HttpExchange exchange; /** * Constructor. * * @param exchange the HTTP exchange. */ public JdkHttpRequest(HttpExchange exchange) { this.exchange = exchange; } @Override public String getHeader(String name) { return exchange.getRequestHeaders().getFirst(name); } @Override public Iterator getHeaderNames() { return exchange.getRequestHeaders().keySet().iterator(); } @Override public Iterator getHeaders(String name) { return exchange.getRequestHeaders().get(name).iterator(); } @Override public InputStream getInputStream() { return exchange.getRequestBody(); } @Override public String getLocalAddress() { return exchange.getLocalAddress().getHostString(); } @Override public String getLocalHostname() { return exchange.getLocalAddress().getHostName(); } @Override public int getLocalPort() { return exchange.getLocalAddress().getPort(); } @Override public String getMethod() { return exchange.getRequestMethod(); } @Override public String getRemoteAddress() { return exchange.getRemoteAddress().getHostString(); } @Override public String getRemoteHostname() { return exchange.getRemoteAddress().getHostName(); } @Override public int getRemotePort() { return exchange.getRemoteAddress().getPort(); } @Override public String getRequestTarget() { return exchange.getRequestURI().toString(); } @Override public String getProtocol() { return exchange.getProtocol(); } @Override public boolean isSecure() { return exchange.getRequestURI().getRawSchemeSpecificPart().equalsIgnoreCase("https"); } } ================================================ FILE: http/jdk/src/main/java/cloud/piranha/http/jdk/JdkHttpResponse.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.jdk; import cloud.piranha.http.api.HttpServerResponse; import com.sun.net.httpserver.HttpExchange; import java.io.IOException; import java.io.OutputStream; /** * The JDK HttpServer variant of a HttpServerResponse. * * @author Manfred Riem (mriem@manorrock.com) */ public class JdkHttpResponse implements HttpServerResponse { /** * Stores the HTTP exchange. */ private HttpExchange exchange; /** * Stores the status. */ private int status; /** * Constructor. * * @param exchange the HTTP exchange. */ public JdkHttpResponse(HttpExchange exchange) { this.exchange = exchange; } @Override public void addHeader(String name, String value) { exchange.getResponseHeaders().add(name, value); } @Override public String getHeader(String name) { return exchange.getResponseHeaders().getFirst(name); } @Override public OutputStream getOutputStream() { return exchange.getResponseBody(); } @Override public void setHeader(String name, String value) { exchange.getResponseHeaders().set(name, value); } @Override public void setStatus(int status) { this.status = status; } @Override public void writeHeaders() throws IOException { exchange.sendResponseHeaders(status, 0); } @Override public void writeStatusLine() throws IOException { // writing the status line is taken care of when writing out the headers. } } ================================================ FILE: http/jdk/src/main/java/cloud/piranha/http/jdk/JdkHttpServer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.jdk; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.api.HttpServerProcessor; import java.io.IOException; import java.net.InetSocketAddress; /** * The JDK implementation of HTTP Server. * * @author Manfred Riem (mriem@manorrock.com) */ public class JdkHttpServer implements HttpServer { /** * Stores the server port number. */ private int serverPort; /** * Stores the HTTP server processor. */ private HttpServerProcessor httpServerProcessor; /** * Stores the SSL enabled flag. */ private boolean ssl; /** * Stores the underlying server. */ private com.sun.net.httpserver.HttpServer server; /** * Constructor */ public JdkHttpServer() { this(8080, null, false); } /** * Constructor. * * @param serverPort the port number. * @param processor the HTTP server processor. * @param ssl the SSL enabled flag. */ public JdkHttpServer(int serverPort, HttpServerProcessor processor, boolean ssl) { this.serverPort = serverPort; this.httpServerProcessor = processor; this.ssl = ssl; } @Override public boolean isRunning() { return server != null; } @Override public void start() { try { if (!ssl) { server = com.sun.net.httpserver.HttpServer.create(); } else { server = com.sun.net.httpserver.HttpsServer.create(); } server.bind(new InetSocketAddress(serverPort), 0); server.createContext("/", new JdkHttpHandler(httpServerProcessor)); server.start(); } catch (IOException ioe) { server = null; } } @Override public void stop() { if (server != null) { server.stop(0); server = null; } } @Override public int getServerPort() { return serverPort; } @Override public void setServerPort(int serverPort) { this.serverPort = serverPort; } @Override public boolean getSSL() { return ssl; } @Override public void setSSL(boolean ssl) { this.ssl = ssl; } @Override public HttpServerProcessor getHttpServerProcessor() { return httpServerProcessor; } @Override public void setHttpServerProcessor(HttpServerProcessor httpServerProcessor) { this.httpServerProcessor = httpServerProcessor; } } ================================================ FILE: http/jdk/src/main/java/cloud/piranha/http/jdk/package-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** *

* This package delivers the JDK implementation of the HTTP Server API. *

* * @author Manfred Riem (mriem@manorrock.com) */ package cloud.piranha.http.jdk; ================================================ FILE: http/jdk/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the JDK HttpServer implementation of the HTTP engine. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.http.jdk { exports cloud.piranha.http.jdk; opens cloud.piranha.http.jdk; requires transitive cloud.piranha.http.api; requires transitive jdk.httpserver; } ================================================ FILE: http/jdk/src/test/java/cloud/piranha/http/jdk/JdkHttpServerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.jdk; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.api.HttpServerProcessor; import cloud.piranha.http.tests.HttpServerTest; import cloud.piranha.http.tests.TestHttpServerProcessor; /** * The JUnit tests for the DefaultHttpServer class. * * @author Manfred Riem (mriem@manorrock.com) */ public class JdkHttpServerTest extends HttpServerTest { /** * Create the server. * * @param portNumber the port number. * @return the HTTP server. */ @Override protected HttpServer createServer(int portNumber) { return new JdkHttpServer(portNumber, new TestHttpServerProcessor(), false); } /** * Create the server. * * @param portNumber the port number. * @param processor the HTTP server processor. * @return the HTTP server. */ @Override protected HttpServer createServer(int portNumber, HttpServerProcessor processor) { return new JdkHttpServer(portNumber, processor, false); } } ================================================ FILE: http/netty/pom.xml ================================================ 4.0.0 cloud.piranha.http project 25.4.0-SNAPSHOT piranha-http-netty jar Piranha - HTTP - Netty Integration cloud.piranha.http piranha-http-api ${project.version} compile io.netty netty-buffer compile io.netty netty-codec-http compile io.netty netty-codec-http2 compile io.netty netty-transport compile cloud.piranha.http piranha-http-tests ${project.version} test ================================================ FILE: http/netty/src/main/java/cloud/piranha/http/netty/NettyHttpServer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.netty; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.api.HttpServerProcessor; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; /** * The Netty implementation of HTTP Server. * * @author Manfred Riem (mriem@manorrock.com) */ public class NettyHttpServer implements HttpServer { /** * Stores the boss event loop group. */ private EventLoopGroup bossGroup; /** * Stores the HTTP server processor. */ private HttpServerProcessor httpServerProcessor; /** * Stores the server port. */ private int serverPort; /** * Stores the worker event loop group. */ private EventLoopGroup workerGroup; /** * Stores the SSL flag */ private boolean ssl; /** * Constructor. */ public NettyHttpServer() { serverPort = 8080; } /** * Constructor. * * @param serverPort the server port. */ public NettyHttpServer(int serverPort) { this.serverPort = serverPort; } /** * Constructor. * * @param serverPort the server port. * @param httpServerProcessor the HTTP server processor. */ public NettyHttpServer(int serverPort, HttpServerProcessor httpServerProcessor) { this.httpServerProcessor = httpServerProcessor; this.serverPort = serverPort; } /** * @see HttpServer#isRunning() */ @Override public boolean isRunning() { return bossGroup != null && workerGroup != null; } /** * @see HttpServer#start() */ @Override public void start() { bossGroup = new NioEventLoopGroup(1); workerGroup = new NioEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new NettyHttpServerInitializer(httpServerProcessor, ssl)) .bind(serverPort).awaitUninterruptibly(); } /** * @see HttpServer#stop() */ @Override public void stop() { workerGroup.shutdownGracefully().awaitUninterruptibly(); bossGroup.shutdownGracefully().awaitUninterruptibly(); bossGroup = null; workerGroup = null; } @Override public int getServerPort() { return serverPort; } @Override public void setServerPort(int serverPort) { this.serverPort = serverPort; } @Override public void setSSL(boolean ssl) { this.ssl = ssl; } @Override public boolean getSSL() { return ssl; } @Override public void setHttpServerProcessor(HttpServerProcessor httpServerProcessor) { this.httpServerProcessor = httpServerProcessor; } @Override public HttpServerProcessor getHttpServerProcessor() { return httpServerProcessor; } } ================================================ FILE: http/netty/src/main/java/cloud/piranha/http/netty/NettyHttpServerHandler.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.netty; import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; import java.lang.System.Logger.Level; import java.lang.System.Logger; import cloud.piranha.http.api.HttpServerProcessor; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; /** * The Netty Handler used by the Netty implementation of HTTP Server. * * @author Manfred Riem (mriem@manorrock.com) */ public class NettyHttpServerHandler extends SimpleChannelInboundHandler { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(NettyHttpServerHandler.class.getName()); /** * Stores the HTTP server processor. */ private final HttpServerProcessor httpServerProcessor; /** * Stores the secure flag. */ private final boolean secure; /** * Constructor. * * @param httpServerProcessor the HTTP server processor. * @param secure the secure flag. */ public NettyHttpServerHandler(HttpServerProcessor httpServerProcessor, boolean secure) { this.httpServerProcessor = httpServerProcessor; this.secure = secure; } /** * Complete the channel read. * * @param context the context. */ @Override public void channelReadComplete(ChannelHandlerContext context) { context.flush(); } /** * Read the channel. * * @param context the context. * @param object the object read. */ @Override protected void channelRead0(ChannelHandlerContext context, FullHttpRequest object) { NettyHttpServerRequest nettyRequest = new NettyHttpServerRequest(context, object, secure); @SuppressWarnings("deprecation") FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, true); NettyHttpServerResponse nettyResponse = new NettyHttpServerResponse(response); httpServerProcessor.process(nettyRequest, nettyResponse); ChannelFuture future = context.channel().writeAndFlush(response); future.addListener(ChannelFutureListener.CLOSE); } /** * Handle the exception. * * @param context the context. * @param throwable the throwable. */ @Override public void exceptionCaught(ChannelHandlerContext context, Throwable throwable) { LOGGER.log(Level.WARNING, "Exception caught in NettyHttpServerHandler", throwable); context.close(); } } ================================================ FILE: http/netty/src/main/java/cloud/piranha/http/netty/NettyHttpServerInitializer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.netty; import static java.lang.System.Logger.Level.ERROR; import java.security.NoSuchAlgorithmException; import java.lang.System.Logger; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import cloud.piranha.http.api.HttpServerProcessor; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.ssl.SslHandler; /** * The Netty Initializer used by the Netty implementation of HTTP Server. * * @author Manfred Riem (mriem@manorrock.com) */ @Sharable public class NettyHttpServerInitializer extends ChannelInitializer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(NettyHttpServerInitializer.class.getName()); /** * Stores the HTTP server processor. */ private final HttpServerProcessor httpServerProcessor; /** * Stores the ssl flag */ private final boolean ssl; /** * Constructor. * * @param httpServerProcessor the HTTP server processor. * @param ssl the ssl flag */ public NettyHttpServerInitializer(HttpServerProcessor httpServerProcessor, boolean ssl) { this.httpServerProcessor = httpServerProcessor; this.ssl = ssl; } /** * Initialize the channel. * * @param channel the channel. */ @Override public void initChannel(SocketChannel channel) { ChannelPipeline pipeline = channel.pipeline(); if (ssl) { try { SSLContext sslContext = SSLContext.getDefault(); SSLEngine sslEngine = sslContext.createSSLEngine(); sslEngine.setUseClientMode(false); pipeline.addLast(new SslHandler(sslEngine)); } catch (NoSuchAlgorithmException e) { LOGGER.log(ERROR, "Unable to match SSL algorithm", e); } } pipeline.addLast(new HttpRequestDecoder()); pipeline.addLast(new HttpResponseEncoder()); pipeline.addLast(new HttpObjectAggregator(10*1024*1024)); pipeline.addLast(new NettyHttpServerHandler(httpServerProcessor, ssl)); } } ================================================ FILE: http/netty/src/main/java/cloud/piranha/http/netty/NettyHttpServerRequest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.netty; import cloud.piranha.http.api.HttpServerRequest; import io.netty.buffer.ByteBufInputStream; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.FullHttpRequest; import java.io.InputStream; import java.net.InetSocketAddress; import java.util.Iterator; /** * The Netty implementation of HttpServerRequest. * * @author Manfred Riem (mriem@manorrock.com) */ public class NettyHttpServerRequest implements HttpServerRequest { /** * Stores the context. */ private final ChannelHandlerContext context; /** * Stores the input stream. */ private InputStream inputStream; /** * Stores the underlying HTTP request. */ private final FullHttpRequest request; /** * Stores the secure flag. */ private final boolean secure; /** * Constructor. * * @param request the HTTP request. * @param context the context. * @param secure the secure flag. */ public NettyHttpServerRequest(ChannelHandlerContext context, FullHttpRequest request, boolean secure) { super(); this.context = context; this.request = request; this.secure = secure; } @Override public String getHeader(String name) { return request.headers().get(name); } @Override public Iterator getHeaderNames() { return request.headers().names().iterator(); } @Override public Iterator getHeaders(String name) { return request.headers().getAll(name).iterator(); } @Override public InputStream getInputStream() { synchronized (request) { if (inputStream == null) { inputStream = new ByteBufInputStream(request.content()); } } return inputStream; } @Override public String getLocalAddress() { InetSocketAddress localAddress = (InetSocketAddress) context.channel().localAddress(); return localAddress.getAddress().getHostAddress(); } @Override public String getLocalHostname() { InetSocketAddress localAddress = (InetSocketAddress) context.channel().localAddress(); return localAddress.getAddress().getHostName(); } @Override public int getLocalPort() { InetSocketAddress localAddress = (InetSocketAddress) context.channel().localAddress(); return localAddress.getPort(); } @Override public String getMethod() { return request.method().name(); } @Override public String getRemoteAddress() { InetSocketAddress remoteAddress = (InetSocketAddress) context.channel().remoteAddress(); return remoteAddress.getAddress().getHostAddress(); } @Override public String getRemoteHostname() { InetSocketAddress remoteAddress = (InetSocketAddress) context.channel().remoteAddress(); return remoteAddress.getAddress().getHostName(); } @Override public int getRemotePort() { InetSocketAddress remoteAddress = (InetSocketAddress) context.channel().remoteAddress(); return remoteAddress.getPort(); } @Override public String getRequestTarget() { return request.uri(); } @Override public String getProtocol() { return request.protocolVersion().text(); } @Override public boolean isSecure() { return secure; } } ================================================ FILE: http/netty/src/main/java/cloud/piranha/http/netty/NettyHttpServerResponse.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.netty; import java.io.IOException; import java.io.OutputStream; import cloud.piranha.http.api.HttpServerResponse; import io.netty.buffer.ByteBufOutputStream; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpResponseStatus; /** * The Netty implementation of HTTP Server Response. * * @author Manfred Riem (mriem@manorrock.com) */ public class NettyHttpServerResponse implements HttpServerResponse { /** * Stores the output stream. */ private OutputStream outputStream; /** * Stores the response. */ private final FullHttpResponse response; /** * Constructor. * * @param response the HTTP response. */ public NettyHttpServerResponse(FullHttpResponse response) { this.response = response; } @Override public void addHeader(String name, String value) { response.headers().add(name, value); } @Override public String getHeader(String name) { return response.headers().get(name); } @Override public OutputStream getOutputStream() { synchronized (response) { if (outputStream == null) { outputStream = new ByteBufOutputStream(response.content()); } } return outputStream; } @Override public void setHeader(String name, String value) { response.headers().set(name, value); } @Override public void setStatus(int status) { response.setStatus(HttpResponseStatus.valueOf(status)); } @Override public void writeHeaders() throws IOException { // writing the headers is taken care of when writing out the response. } @Override public void writeStatusLine() throws IOException { // writing the status line is taken care of when writing out the response. } } ================================================ FILE: http/netty/src/main/java/cloud/piranha/http/netty/package-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** *

* This package delivers a Netty implementation of the HTTP Server API. *

* * @author Manfred Riem (mriem@manorrock.com) */ package cloud.piranha.http.netty; ================================================ FILE: http/netty/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Netty implementation of the HTTP engine. * *

* This module integrates Netty into Piranha. See * https://github.com/netty/netty for more information about its project. *

* * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.http.netty { exports cloud.piranha.http.netty; opens cloud.piranha.http.netty; requires transitive cloud.piranha.http.api; requires io.netty.buffer; requires io.netty.codec; requires transitive io.netty.codec.http; requires io.netty.codec.http2; requires io.netty.common; requires io.netty.handler; requires transitive io.netty.transport; } ================================================ FILE: http/netty/src/test/java/cloud/piranha/http/netty/NettyHttpServerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.netty; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.api.HttpServerProcessor; import cloud.piranha.http.tests.HttpServerTest; import cloud.piranha.http.tests.TestHttpServerProcessor; /** * The JUnit tests for the NettyHttpServer class. * * @author Manfred Riem (mriem@manorrock.com) */ public class NettyHttpServerTest extends HttpServerTest { /** * Create the Netty HTTP server. * * @param portNumber the port number. * @return the Netty HTTP server. */ @Override protected HttpServer createServer(int portNumber) { NettyHttpServer server = new NettyHttpServer(portNumber); server.setHttpServerProcessor(new TestHttpServerProcessor()); return server; } /** * Create the Netty HTTP server. * * @param portNumber the port number. * @param processor the HTTP server processor. * @return the Netty HTTP server. */ @Override protected HttpServer createServer(int portNumber, HttpServerProcessor processor) { return new NettyHttpServer(portNumber, processor); } } ================================================ FILE: http/pom.xml ================================================ 4.0.0 cloud.piranha project 25.4.0-SNAPSHOT cloud.piranha.http project pom Piranha - HTTP api crac grizzly impl jdk netty tests undertow virtual webapp cloud.piranha bom ${project.version} pom import org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test default file:///tmp/piranha/http/ ================================================ FILE: http/tests/pom.xml ================================================ 4.0.0 cloud.piranha.http project 25.4.0-SNAPSHOT piranha-http-tests jar Piranha - HTTP - Tests cloud.piranha.http piranha-http-api ${project.version} compile me.alexpanov free-port-finder compile org.junit.jupiter junit-jupiter-api compile org.junit-pioneer junit-pioneer compile ================================================ FILE: http/tests/src/main/java/cloud/piranha/http/tests/HttpServerRequestTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.tests; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.api.HttpServerProcessor; import static cloud.piranha.http.api.HttpServerProcessorEndState.COMPLETED; import cloud.piranha.http.api.HttpServerRequest; import cloud.piranha.http.api.HttpServerResponse; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse.BodyHandlers; import me.alexpanov.net.FreePortFinder; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the HttpServerRequest class. * * @author Manfred Riem (mriem@manorrock.com) */ public abstract class HttpServerRequestTest { /** * Constructor. */ public HttpServerRequestTest() { } /** * Create server with a port and processor. * * @param portNumber the port number. * @param processor the HTTP processor. * @return the HTTP server. */ protected abstract HttpServer createServer(int portNumber, HttpServerProcessor processor); /** * Test getRequestTarget method. */ @Test void testGetRequestTarget() { System.clearProperty("requestTarget"); int port = FreePortFinder.findFreeLocalPort(); HttpServer server = createServer(port, (HttpServerRequest request, HttpServerResponse response) -> { System.setProperty("requestTarget", request.getRequestTarget()); return COMPLETED; }); server.start(); try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder( URI.create("http://localhost:" + port)).build(); client.send(request, BodyHandlers.discarding()); } catch (Exception e) { // do nothing } finally { server.stop(); } assertNotNull(System.getProperty("requestTarget")); System.clearProperty("requestTarget"); } /** * Test getRequestTarget method (looking for query string marker). */ @Test void testGetRequestTarget2() { System.clearProperty("requestTarget"); int port = FreePortFinder.findFreeLocalPort(); HttpServer server = createServer(port, (HttpServerRequest request, HttpServerResponse response) -> { System.setProperty("requestTarget", request.getRequestTarget()); return COMPLETED; }); server.start(); try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder( URI.create("http://localhost:" + port + "?queryParam=queryParam")).build(); client.send(request, BodyHandlers.discarding()); } catch (Exception e) { // do nothing } finally { server.stop(); } assertNotNull(System.getProperty("requestTarget")); assertTrue(System.getProperty("requestTarget").indexOf("?") > 0); System.clearProperty("requestTarget"); } } ================================================ FILE: http/tests/src/main/java/cloud/piranha/http/tests/HttpServerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.tests; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.api.HttpServerProcessor; import cloud.piranha.http.api.HttpServerProcessorEndState; import static cloud.piranha.http.api.HttpServerProcessorEndState.COMPLETED; import cloud.piranha.http.api.HttpServerRequest; import cloud.piranha.http.api.HttpServerResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.util.Iterator; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; import me.alexpanov.net.FreePortFinder; /** * An abstract JUnit test for any HttpServer implementation. * * @author Manfred Riem (mriem@manorrock.com) */ public abstract class HttpServerTest { /** * Stores the keep alive 'close' constant. */ private static final String CLOSE = "close"; /** * Stores the 'Content-Type' header constant. */ private static final String CONTENT_TYPE = "Content-Type"; /** * Stores the 'Keep-Alive' header constant. */ private static final String KEEP_ALIVE = "Keep-Alive"; /** * Stores the 'http://localhost:' prefix. */ private static final String HTTP_LOCALHOST_PREFIX = "http://localhost:"; /** * Stores the 'text/plain' content type. */ private static final String TEXT_PLAIN = "text/plain"; /** * Constructor. */ public HttpServerTest() { } /** * Create server with a port. * * @param portNumber the port number. * @return the HTTP server. */ protected abstract HttpServer createServer(int portNumber); /** * Create server with a port and processor. * * @param portNumber the port number. * @param processor the HTTP processor. * @return the HTTP server. */ protected abstract HttpServer createServer(int portNumber, HttpServerProcessor processor); /** * Test addHeader method. */ @Test void testAddHeader() { int port = findPort(); HttpServer server = createServer(port, (HttpServerRequest request, HttpServerResponse response) -> { try { response.setStatus(200); response.setHeader(CONTENT_TYPE, TEXT_PLAIN); response.setHeader(KEEP_ALIVE, CLOSE); response.writeStatusLine(); response.writeHeaders(); OutputStream outputStream = response.getOutputStream(); Iterator headerNames = request.getHeaderNames(); while (headerNames.hasNext()) { String name = headerNames.next(); outputStream.write(name.toLowerCase().getBytes()); outputStream.write("\n".getBytes()); outputStream.write(request.getHeader(name).getBytes()); outputStream.write("\n".getBytes()); } outputStream.flush(); } catch (IOException ioe) { // nothing to do here. } return COMPLETED; }); server.start(); try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder(URI.create(HTTP_LOCALHOST_PREFIX + port)).header("name", "value1").build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); assertEquals(200, response.statusCode()); String body = response.body(); assertTrue(body.contains("name")); assertTrue(body.contains("value1")); } catch (IOException | InterruptedException e) { throw new RuntimeException(e); } finally { server.stop(); } } /** * Test file. * * @throws Exception when a serious error occurs. */ @Test void testFile() throws Exception { int port = findPort(); HttpServer server = createServer(port); server.start(); try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder(URI.create(HTTP_LOCALHOST_PREFIX + port + "/pom.xml")).build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); assertEquals(200, response.statusCode()); String responseText = response.body(); assertTrue(responseText.contains("modelVersion")); } catch (IOException ioe) { throw new RuntimeException(ioe); } finally { server.stop(); } } /** * Test file not found. * * @throws Exception when an error occurs. */ @Test void testFileNotFound() throws Exception { int port = findPort(); HttpServer server = createServer(port); server.start(); try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder(URI.create(HTTP_LOCALHOST_PREFIX + port + "/this_is_certainly_not_there")).build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.discarding()); assertEquals(404, response.statusCode()); } catch (IOException ioe) { throw new RuntimeException(ioe); } finally { server.stop(); } } /** * Test getLocalAddress method. */ @Test void testGetLocalAddress() { int port = findPort(); HttpServer server = createServer(port, (HttpServerRequest request, HttpServerResponse response) -> { try { response.setStatus(200); response.setHeader(CONTENT_TYPE, TEXT_PLAIN); response.setHeader(KEEP_ALIVE, CLOSE); response.writeStatusLine(); response.writeHeaders(); String value = request.getLocalAddress(); OutputStream outputStream = response.getOutputStream(); outputStream.write(value.getBytes()); outputStream.flush(); } catch (IOException ioe) { // nothing to do here. } return COMPLETED; }); server.start(); try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder(URI.create(HTTP_LOCALHOST_PREFIX + port)).build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); assertEquals(200, response.statusCode()); String body = response.body(); assertTrue(body.contains("127.0.0.1")); } catch (IOException | InterruptedException e) { throw new RuntimeException(e); } finally { server.stop(); } } /** * Test processing. * * @throws Exception when an error occurs. */ @Test void testProcessing() throws Exception { int port = findPort(); HttpServer server = createServer(port); server.start(); try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder(URI.create(HTTP_LOCALHOST_PREFIX + port)).build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.discarding()); assertEquals(200, response.statusCode()); } catch (IOException ioe) { throw new RuntimeException(ioe); } finally { server.stop(); } } /** * Test processing. * * @throws Exception when an error occurs. */ @Test void testProcessing2() throws Exception { int port = findPort(); HttpServer server = createServer(port); server.start(); try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder(URI.create(HTTP_LOCALHOST_PREFIX + port)).build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.discarding()); assertEquals(200, response.statusCode()); } catch (IOException ioe) { throw new RuntimeException(ioe); } finally { server.stop(); } } /** * Test start and stop method. * * @throws Exception when an error occurs. */ @Test void testStartAndStop() throws Exception { int port = findPort(); HttpServer server = createServer(port); server.start(); assertTrue(server.isRunning()); server.stop(); assertFalse(server.isRunning()); } /** * Test if the server supports HTTP/1.0. * * @throws Exception when an error occurs. */ void testRequestHTTP10() throws Exception { int port = findPort(); HttpServer server = createServer(port, HttpServerTest::returnProtocol); server.start(); try ( Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(), port); OutputStream outputStream = socket.getOutputStream()) { outputStream.write(("GET / HTTP/1.0\r\nHost: localhost:" + port + "\r\n\r\n").getBytes(StandardCharsets.UTF_8)); outputStream.flush(); InputStream inputStream = socket.getInputStream(); ByteArrayOutputStream response = new ByteArrayOutputStream(); inputStream.transferTo(response); assertTrue(response.toString(StandardCharsets.UTF_8).contains("HTTP/1.0")); } Thread.sleep(5000); server.stop(); } /** * Test if the server supports HTTP/1.1. * * @throws Exception when an error occurs. */ void testRequestHTTP11() throws Exception { int port = findPort(); HttpServer server = createServer(port, HttpServerTest::returnProtocol); server.start(); try (Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(), port); OutputStream outputStream = socket.getOutputStream()) { outputStream.write(("GET / HTTP/1.1\r\nHost: localhost:" + port + "\r\n\r\n").getBytes(StandardCharsets.UTF_8)); outputStream.flush(); InputStream inputStream = socket.getInputStream(); ByteArrayOutputStream response = new ByteArrayOutputStream(); inputStream.transferTo(response); assertTrue(response.toString(StandardCharsets.UTF_8).contains("HTTP/1.1")); } Thread.sleep(5000); server.stop(); } /** * Return the HTTP protocol. * * @param request the request. * @param response the response. * @return state. */ private static HttpServerProcessorEndState returnProtocol(HttpServerRequest request, HttpServerResponse response) { try { response.setStatus(200); response.setHeader(CONTENT_TYPE, TEXT_PLAIN); response.setHeader(KEEP_ALIVE, CLOSE); response.writeStatusLine(); response.writeHeaders(); OutputStream outputStream = response.getOutputStream(); outputStream.write(request.getProtocol().getBytes()); outputStream.flush(); } catch (IOException ioe) { } return COMPLETED; } private static int findPort() { return FreePortFinder.findFreeLocalPort(); } } ================================================ FILE: http/tests/src/main/java/cloud/piranha/http/tests/TestHttpServerProcessor.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.tests; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.lang.System.Logger; import cloud.piranha.http.api.HttpServerProcessor; import cloud.piranha.http.api.HttpServerProcessorEndState; import static cloud.piranha.http.api.HttpServerProcessorEndState.COMPLETED; import cloud.piranha.http.api.HttpServerRequest; import cloud.piranha.http.api.HttpServerResponse; import static java.lang.System.Logger.Level.ERROR; /** * The default implementation of a HTTP Server Processor. * *

* This HTTP Server Processor will either show the user with a directory * listing, or stream back contents of the file clicked on in the browser, or it * will return a 404 error because neither could be found. *

* * @author Manfred Riem (mriem@manorrock.com) */ public class TestHttpServerProcessor implements HttpServerProcessor { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(TestHttpServerProcessor.class.getName()); /** * Stores the error writing response message. */ private static final String IO_ERROR_WRITING_RESPONSE = "An I/O error occurred while writing the response"; /** * Constructor. */ public TestHttpServerProcessor() { } @Override public HttpServerProcessorEndState process(HttpServerRequest request, HttpServerResponse response) { response.setStatus(200); File baseDir = new File(System.getProperty("user.dir")); File file = new File(baseDir, request.getRequestTarget()); if (file.exists() && file.isDirectory() && "GET".equals(request.getMethod())) { response.setHeader("Content-Type", "text/html"); try { response.writeStatusLine(); response.writeHeaders(); OutputStream outputStream = response.getOutputStream(); PrintWriter writer = new PrintWriter(outputStream); writer.println(""); writer.println(file.getName() + "
"); writer.print(".
"); writer.print("..
"); File[] fileList = file.listFiles(); if (fileList != null && fileList.length > 0) { for (int i = 0; i < fileList.length; i++) { String uri = fileList[i].getAbsolutePath().substring(baseDir.getAbsolutePath().length()); if (uri.startsWith("//")) { uri = uri.substring(1); } writer.println("" + fileList[i].getName() + "
" ); } } writer.println(""); writer.flush(); } catch (IOException exception) { LOGGER.log(ERROR, IO_ERROR_WRITING_RESPONSE, exception); } } else if (file.exists() && !file.isDirectory()) { response.setHeader("Content-Type", "application/octet-stream"); response.setHeader("Content-Length", Long.toString(file.length())); try { response.writeStatusLine(); response.writeHeaders(); OutputStream outputStream = response.getOutputStream(); try ( InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) { int read = inputStream.read(); while (read != -1) { outputStream.write((char) read); read = inputStream.read(); } outputStream.flush(); } } catch (IOException exception) { LOGGER.log(ERROR, IO_ERROR_WRITING_RESPONSE, exception); } } else { try { response.setStatus(404); response.writeStatusLine(); response.writeHeaders(); } catch (IOException exception) { LOGGER.log(ERROR, IO_ERROR_WRITING_RESPONSE, exception); } } return COMPLETED; } } ================================================ FILE: http/tests/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the test framework that can be used by any HTTP * implementations for testing the implementation. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.http.tests { exports cloud.piranha.http.tests; opens cloud.piranha.http.tests; requires transitive cloud.piranha.http.api; requires free.port.finder; requires java.net.http; requires static org.apiguardian.api; requires static org.junit.jupiter.api; requires org.junitpioneer; } ================================================ FILE: http/undertow/pom.xml ================================================ 4.0.0 cloud.piranha.http project 25.4.0-SNAPSHOT piranha-http-undertow jar Piranha - HTTP - Undertow Integration cloud.piranha.http piranha-http-api ${project.version} compile io.undertow undertow-core compile cloud.piranha.http piranha-http-tests ${project.version} test ================================================ FILE: http/undertow/src/main/java/cloud/piranha/http/undertow/UndertowHttpHandler.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.undertow; import cloud.piranha.http.api.HttpServerProcessor; import io.undertow.server.HttpHandler; import io.undertow.server.HttpServerExchange; /** * The Undertow HttpHandler used by the Undertow implementation of HTTP Server. * * @author Manfred Riem (mriem@manorrock.com) */ public class UndertowHttpHandler implements HttpHandler { /** * Stores the HTTP server processor. */ private final HttpServerProcessor httpServerProcessor; /** * Constructor. * * @param httpServerProcessor the HTTP server processor. */ public UndertowHttpHandler(HttpServerProcessor httpServerProcessor) { this.httpServerProcessor = httpServerProcessor; } /** * Handle the request. * * @param exchange the HTTP server exchange. * @throws Exception when a serious error occurs. */ @Override public void handleRequest(HttpServerExchange exchange) throws Exception { if (exchange.isInIoThread()) { exchange.dispatch(this); return; } httpServerProcessor.process( new UndertowHttpRequest(exchange), new UndertowHttpResponse(exchange)); } } ================================================ FILE: http/undertow/src/main/java/cloud/piranha/http/undertow/UndertowHttpRequest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.undertow; import java.io.InputStream; import java.util.ArrayList; import java.util.Iterator; import cloud.piranha.http.api.HttpServerRequest; import io.undertow.server.HttpServerExchange; import io.undertow.util.HttpString; /** * The Undertow implementation of HttpServerRequest. * * @author Manfred Riem (mriem@manorrock.com) */ public class UndertowHttpRequest implements HttpServerRequest { /** * Stores the HTTP server exchange. */ private final HttpServerExchange exchange; /** * Stores the header names. */ private ArrayList headerNames; /** * Constructor. * * @param exchange the HTTP server exchange. */ public UndertowHttpRequest(HttpServerExchange exchange) { this.exchange = exchange; } @Override public String getHeader(String name) { return exchange.getRequestHeaders().getFirst(name); } @Override public Iterator getHeaderNames() { if (headerNames == null) { headerNames = new ArrayList<>(); for (HttpString httpString : exchange.getRequestHeaders().getHeaderNames()) { headerNames.add(httpString.toString()); } } return headerNames.iterator(); } @Override public Iterator getHeaders(String name) { return exchange.getRequestHeaders().get(name).iterator(); } @Override public InputStream getInputStream() { if (!exchange.isBlocking()) { exchange.startBlocking(); } return exchange.getInputStream(); } @Override public String getLocalAddress() { return exchange.getDestinationAddress().getAddress().getHostAddress(); } @Override public String getLocalHostname() { return exchange.getHostName(); } @Override public int getLocalPort() { return exchange.getHostPort(); } @Override public String getMethod() { return exchange.getRequestMethod().toString(); } @Override public String getRemoteAddress() { return exchange.getSourceAddress().getAddress().getHostAddress(); } @Override public String getRemoteHostname() { return exchange.getSourceAddress().getAddress().getHostName(); } @Override public int getRemotePort() { return exchange.getSourceAddress().getPort(); } @Override public String getRequestTarget() { return exchange.getRequestURI(); } @Override public String getProtocol() { return exchange.getProtocol().toString(); } @Override public boolean isSecure() { return exchange.isSecure(); } } ================================================ FILE: http/undertow/src/main/java/cloud/piranha/http/undertow/UndertowHttpResponse.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.undertow; import java.io.IOException; import java.io.OutputStream; import cloud.piranha.http.api.HttpServerResponse; import io.undertow.server.HttpServerExchange; import io.undertow.util.HttpString; /** * The Undertow implementation of HTTP Server Response. * * @author Manfred Riem (mriem@manorrock.com) */ public class UndertowHttpResponse implements HttpServerResponse { /** * Stores the HTTP server exchange. */ private final HttpServerExchange exchange; /** * Constructor. * * @param exchange the HTTP server exchange. */ public UndertowHttpResponse(HttpServerExchange exchange) { this.exchange = exchange; } @Override public void addHeader(String name, String value) { exchange.getResponseHeaders().add(HttpString.tryFromString(name), value); } @Override public String getHeader(String name) { return exchange.getResponseHeaders().getFirst(name); } @Override public OutputStream getOutputStream() { if (!exchange.isBlocking()) { exchange.startBlocking(); } return exchange.getOutputStream(); } @Override public void setHeader(String name, String value) { exchange.getResponseHeaders().put(HttpString.tryFromString(name), value); } @Override public void setStatus(int status) { exchange.setStatusCode(status); } @Override public void writeHeaders() throws IOException { // writing the headers is taken care of when writing out the response. } @Override public void writeStatusLine() throws IOException { // writing the status line is taken care of when writing out the response. } } ================================================ FILE: http/undertow/src/main/java/cloud/piranha/http/undertow/UndertowHttpServer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.undertow; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.api.HttpServerProcessor; import io.undertow.Undertow; import io.undertow.UndertowOptions; import java.security.NoSuchAlgorithmException; import java.lang.System.Logger; import javax.net.ssl.SSLContext; import static java.lang.System.Logger.Level.ERROR; /** * The Undertow implementation of HTTP Server. * * @author Manfred Riem (mriem@manorrock.com) */ public class UndertowHttpServer implements HttpServer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(UndertowHttpServer.class.getName()); /** * Stores the HTTP server processor. */ private HttpServerProcessor httpServerProcessor; /** * Stores the server port. */ private int serverPort; /** * * * Stores the SSL flag */ private boolean ssl; /** * Stores the Undertow server. */ private Undertow undertow; /** * Constructor. */ public UndertowHttpServer() { serverPort = 8080; } /** * Constructor. * * @param serverPort the server port. */ public UndertowHttpServer(int serverPort) { this.serverPort = serverPort; } /** * Constructor. * * @param serverPort the server port. * @param httpServerProcessor the HTTP server processor. */ public UndertowHttpServer(int serverPort, HttpServerProcessor httpServerProcessor) { this.httpServerProcessor = httpServerProcessor; this.serverPort = serverPort; } /** * {@return the HTTP server processor} */ @Override public HttpServerProcessor getHttpServerProcessor() { return httpServerProcessor; } /** * {@return the SSL boolean flag} */ @Override public boolean getSSL() { return ssl; } /** * {@return the server port} */ @Override public int getServerPort() { return serverPort; } /** * Is the server running? * * @return true if running, false otherwise. */ @Override public boolean isRunning() { return undertow != null; } /** * Set the HTTP server processor. * * @param httpServerProcessor the HTTP server processor. */ @Override public void setHttpServerProcessor(HttpServerProcessor httpServerProcessor) { this.httpServerProcessor = httpServerProcessor; } /** * Set the SSL boolean flag. * * @param ssl the SSL boolean flag. */ @Override public void setSSL(boolean ssl) { this.ssl = ssl; } /** * Set the server port. * * @param serverPort the server port. */ @Override public void setServerPort(int serverPort) { this.serverPort = serverPort; } /** * Start the server. */ @Override public void start() { Undertow.Builder builder = Undertow.builder() .setHandler(new UndertowHttpHandler(httpServerProcessor)); if (ssl) { try { builder.addHttpsListener(serverPort, "0.0.0.0", SSLContext.getDefault()); } catch (NoSuchAlgorithmException e) { LOGGER.log(ERROR, "Unable to match SSL algorithm", e); } } else { builder.addHttpListener(serverPort, "0.0.0.0"); } builder = builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true); builder = builder.setServerOption(UndertowOptions.SHUTDOWN_TIMEOUT, 5000); undertow = builder.build(); undertow.start(); } /** * Stops the server. */ @Override public void stop() { undertow.stop(); undertow = null; } } ================================================ FILE: http/undertow/src/main/java/cloud/piranha/http/undertow/package-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** *

* This package delivers an Undertow implementation of the HTTP Server API. *

* * @author Manfred Riem (mriem@manorrock.com) */ package cloud.piranha.http.undertow; ================================================ FILE: http/undertow/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Undertow implementation of the HTTP engine. * *

* This module integrates WildFly Undertow into Piranha. See * https://github.com/undertow-io/undertow for more information about its * project. *

* * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.http.undertow { exports cloud.piranha.http.undertow; opens cloud.piranha.http.undertow; requires cloud.piranha.http.api; requires jdk.unsupported; requires undertow.core; } ================================================ FILE: http/undertow/src/test/java/cloud/piranha/http/undertow/UndertowHttpServerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.undertow; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.api.HttpServerProcessor; import cloud.piranha.http.tests.HttpServerTest; import cloud.piranha.http.tests.TestHttpServerProcessor; /** * The JUnit tests for the UndertowHttpServer class. * * @author Manfred Riem (mriem@manorrock.com) */ public class UndertowHttpServerTest extends HttpServerTest { /** * Create the Undertow HTTP server. * * @param portNumber the port number. * @return the Undertow HTTP server. */ @Override protected HttpServer createServer(int portNumber) { UndertowHttpServer server = new UndertowHttpServer(portNumber); server.setHttpServerProcessor(new TestHttpServerProcessor()); return server; } /** * Create the Undertow HTTP server. * * @param portNumber the port number. * @param processor the HTTP server processor. * @return the Undertow HTTP server. */ @Override protected HttpServer createServer(int portNumber, HttpServerProcessor processor) { return new UndertowHttpServer(portNumber, processor); } } ================================================ FILE: http/virtual/pom.xml ================================================ 4.0.0 cloud.piranha.http project 25.4.0-SNAPSHOT piranha-http-virtual Piranha - HTTP - Virtual Threads implementation cloud.piranha.http piranha-http-impl ${project.version} cloud.piranha.http piranha-http-tests ${project.version} test org.apache.maven.plugins maven-javadoc-plugin org.apache.maven.plugins maven-surefire-plugin ${argLine} --enable-preview jdk21 21 org.apache.maven.plugins maven-compiler-plugin ${java.version} true org.apache.maven.plugins maven-javadoc-plugin attach-javadocs --enable-preview ${java.version} jdk23 23 org.apache.maven.plugins maven-compiler-plugin 23 true org.apache.maven.plugins maven-javadoc-plugin attach-javadocs --enable-preview 23 ================================================ FILE: http/virtual/src/main/java/cloud/piranha/http/virtual/VirtualHttpServer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.virtual; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.api.HttpServerProcessor; import cloud.piranha.http.impl.DefaultHttpServerRequest; import cloud.piranha.http.impl.DefaultHttpServerResponse; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.security.NoSuchAlgorithmException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; import static java.lang.System.Logger.Level.WARNING; /** * Implementation of HttpServer that uses virtual threads */ public class VirtualHttpServer implements HttpServer { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(VirtualHttpServer.class.getName()); /** * Stores the executor service. */ private final ExecutorService executorService = Executors.newSingleThreadExecutor(); /** * Stores the running flag */ private boolean isRunning; /** * Stores the HTTP server processor */ private HttpServerProcessor httpServerProcessor; /** * Stores the server port */ private int serverPort; /** * Stores the SSL flag */ private boolean ssl; /** * Constructor */ public VirtualHttpServer() { this(8080); } /** * Constructor * * @param serverPort the server port */ public VirtualHttpServer(int serverPort) { this(serverPort, null, false); } /** * Constructor * * @param serverPort server port * @param httpServerProcessor the HTTP server processor * @param ssl true if SSL is enabled */ public VirtualHttpServer(int serverPort, HttpServerProcessor httpServerProcessor, boolean ssl) { this.httpServerProcessor = httpServerProcessor; this.serverPort = serverPort; this.ssl = ssl; } @Override public void start() { isRunning = true; CountDownLatch barrier = new CountDownLatch(1); executorService.execute(() -> { try { ServerSocket serverSocket = getServerSocket(); barrier.countDown(); serve(serverSocket); } catch (Exception e) { isRunning = false; LOGGER.log(WARNING, e); throw new RuntimeException(e); } }); try { barrier.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } } /** * Create a server socket * @return the server socket * @throws Exception if an error occurs */ private ServerSocket getServerSocket() throws Exception { try { ServerSocket serverSocket; if (ssl) { SSLContext context = SSLContext.getDefault(); SSLEngine engine = context.createSSLEngine(); SSLServerSocketFactory factory = context.getServerSocketFactory(); SSLServerSocket socket = (SSLServerSocket) factory.createServerSocket(getServerPort()); SSLParameters parameters = new SSLParameters(); parameters.setCipherSuites(engine.getSupportedCipherSuites()); parameters.setProtocols(engine.getSupportedProtocols()); parameters.setNeedClientAuth(false); parameters.setWantClientAuth(true); socket.setSSLParameters(parameters); serverSocket = socket; } else { serverSocket = new ServerSocket(getServerPort()); } serverSocket.setReuseAddress(true); return serverSocket; } catch (IOException exception) { throw new IOException("An I/O error occurred while starting the HTTP server", exception); } catch (NoSuchAlgorithmException ex) { throw new NoSuchAlgorithmException("Unable to match SSL algorithm", ex); } } @Override public void stop() { isRunning = false; executorService.shutdown(); } /** * Handle requests * @param serverSocket the server socket * @throws IOException if an error occurs * @throws InterruptedException if an error occurs */ private void serve(ServerSocket serverSocket) throws IOException, InterruptedException { try (ExecutorService virtualExecutor = Executors.newVirtualThreadPerTaskExecutor()) { while (isRunning()) { Socket socket = serverSocket.accept(); virtualExecutor.execute(() -> { try { handle(socket); } catch (IOException ioe) { LOGGER.log(WARNING, ioe); } }); } } } /** * Handle a request * @param socket the socket * @return null * @throws IOException if an error occurs */ private Void handle(Socket socket) throws IOException { DefaultHttpServerRequest defaultHttpServerRequest = new DefaultHttpServerRequest(socket); DefaultHttpServerResponse defaultHttpServerResponse = new DefaultHttpServerResponse(socket); getHttpServerProcessor().process(defaultHttpServerRequest, defaultHttpServerResponse); defaultHttpServerResponse.closeResponse(); return null; } @Override public boolean isRunning() { return isRunning; } @Override public int getServerPort() { return serverPort; } @Override public HttpServerProcessor getHttpServerProcessor() { return httpServerProcessor; } @Override public void setHttpServerProcessor(HttpServerProcessor httpServerProcessor) { this.httpServerProcessor = httpServerProcessor; } @Override public void setServerPort(int serverPort) { this.serverPort = serverPort; } @Override public void setSSL(boolean ssl) { this.ssl = ssl; } @Override public boolean getSSL() { return ssl; } } ================================================ FILE: http/virtual/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Virtual Threads implementation of the HTTP engine. * * @author Thiago * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.http.virtual { exports cloud.piranha.http.virtual; opens cloud.piranha.http.virtual; requires transitive cloud.piranha.http.api; requires cloud.piranha.http.impl; } ================================================ FILE: http/virtual/src/test/java/cloud/piranha/http/virtual/VirtualHttpServerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.virtual; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.api.HttpServerProcessor; import cloud.piranha.http.tests.HttpServerTest; import cloud.piranha.http.tests.TestHttpServerProcessor; /** * The JUnit tests for the DefaultHttpServer class. * * @author Manfred Riem (mriem@manorrock.com) */ public class VirtualHttpServerTest extends HttpServerTest { /** * Create the server. * * @param portNumber the port number. * @return the HTTP server. */ @Override protected HttpServer createServer(int portNumber) { return new VirtualHttpServer(portNumber, new TestHttpServerProcessor(), false); } /** * Create the server. * * @param portNumber the port number. * @param processor the HTTP server processor. * @return the HTTP server. */ @Override protected HttpServer createServer(int portNumber, HttpServerProcessor processor) { return new VirtualHttpServer(portNumber, processor, false); } } ================================================ FILE: http/webapp/pom.xml ================================================ 4.0.0 cloud.piranha.http project 25.4.0-SNAPSHOT piranha-http-webapp jar Piranha - HTTP - WebApplication Integration cloud.piranha.core piranha-core-impl ${project.version} compile cloud.piranha.http piranha-http-api ${project.version} compile cloud.piranha.http piranha-http-impl ${project.version} test org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test org.apache.maven.plugins maven-compiler-plugin --add-modules=java.net.http org.apache.maven.plugins maven-surefire-plugin --add-opens cloud.piranha.http.webapp/cloud.piranha.http.webapp=ALL-UNNAMED --add-reads cloud.piranha.http.webapp=java.net.http ================================================ FILE: http/webapp/src/main/java/cloud/piranha/http/webapp/HttpWebApplicationOutputStream.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.webapp; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationResponse; import cloud.piranha.core.impl.DefaultWebApplicationOutputStream; import cloud.piranha.http.api.HttpServerResponse; import jakarta.servlet.ServletRequest; import static jakarta.servlet.http.HttpServletResponse.SC_SWITCHING_PROTOCOLS; import java.io.IOException; import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Collection; /** * The HttpWebApplication variant of WebApplicationOutputStream. * * @author Manfred Riem (mriem@manorrock.com) */ public class HttpWebApplicationOutputStream extends DefaultWebApplicationOutputStream { /** * Stores the HttpServerResponse. */ private final HttpServerResponse httpServerResponse; /** * Constructor. * * @param response the WebApplicationResponse. * @param httpServerResponse the HttpServerResponse. */ public HttpWebApplicationOutputStream( WebApplicationResponse response, HttpServerResponse httpServerResponse) { this.response = response; this.httpServerResponse = httpServerResponse; setOutputStream(httpServerResponse.getOutputStream()); } @Override public void close() throws IOException { super.close(); /* * If we were upgraded and we are now closing the output stream it means * we are done so we should unlink the request and response. */ if (response.getStatus() == SC_SWITCHING_PROTOCOLS) { WebApplication webApplication = response.getWebApplication(); ServletRequest request = webApplication.getRequest(response); webApplication.unlinkRequestAndResponse(request, response); } } /** * Format the timestamp to a GMT string. * * @param timestamp the timestamp. * @return the GMT string. */ private String formatDateToGMT(long timestamp) { return Instant.ofEpochMilli(timestamp).atZone(ZoneId.of("GMT")) .format(DateTimeFormatter.RFC_1123_DATE_TIME); } @Override public void writeStatusLine() throws IOException { httpServerResponse.setStatus(response.getStatus()); httpServerResponse.writeStatusLine(); } @SuppressWarnings("removal") @Override public void writeHeaders() throws IOException { /* * Add Content-Type. */ if (response.getContentType() != null) { StringBuilder contentType = new StringBuilder(); contentType.append(response.getContentType()); httpServerResponse.addHeader("Content-Type", contentType.toString()); } /** * Add Content-Language. */ if (response.getContentLanguage() != null) { httpServerResponse.addHeader("Content-Language", response.getContentLanguage()); } /** * Add cookies. */ response.getCookies().forEach(cookie -> { StringBuilder value = new StringBuilder(); value.append(cookie.getName()) .append("="); if (cookie.getValue() != null) { value.append(cookie.getValue()); } if (cookie.getMaxAge() > -1) { value.append("; Max-Age=").append(cookie.getMaxAge()); String expireDate = formatDateToGMT(Instant.now().plusSeconds(cookie.getMaxAge()).toEpochMilli()); value.append("; Expires=").append(expireDate); } if (cookie.getSecure()) { value.append("; Secure"); } if (cookie.isHttpOnly()) { value.append("; HttpOnly"); } if (cookie.getPath() != null) { value.append("; Path=").append(cookie.getPath()); } if (cookie.getVersion() > 0) { value.append("; Version=").append(cookie.getVersion()); } httpServerResponse.addHeader("Set-Cookie", value.toString()); }); /** * Add remaining headers. */ response.getHeaderNames().forEach( name -> { Collection values = response.getHeaders(name); values.forEach(value -> httpServerResponse.addHeader(name, value)); }); /** * Write the headers to the HttpServerResponse. */ httpServerResponse.writeHeaders(); } } ================================================ FILE: http/webapp/src/main/java/cloud/piranha/http/webapp/HttpWebApplicationRequest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.webapp; import cloud.piranha.core.impl.CookieParser; import cloud.piranha.core.impl.DefaultWebApplicationInputStream; import cloud.piranha.http.api.HttpServerRequest; import cloud.piranha.core.impl.DefaultWebApplicationRequest; import java.util.Iterator; import java.util.stream.Stream; /** * The HttpServerRequest variant of WebApplicationRequest. * * @author Manfred Riem (mriem@manorrock.com) */ public class HttpWebApplicationRequest extends DefaultWebApplicationRequest { /** * Stores the wrapped HttpServerRequest. */ private final HttpServerRequest wrapped; /** * Constructor. * * @param wrapped the wrapped HttpServerRequest. */ public HttpWebApplicationRequest(HttpServerRequest wrapped) { this.wrapped = wrapped; populateRequest(wrapped); } /** * Populate the request. * * @param serverRequest the HTTP server request. */ private void populateRequest(HttpServerRequest serverRequest) { if (serverRequest.getRequestTarget() != null && serverRequest.getRequestTarget().contains("?")) { String requestTarget = serverRequest.getRequestTarget(); contextPath = requestTarget.substring(0, requestTarget.indexOf("?")); queryString = requestTarget.substring(requestTarget.indexOf("?") + 1); } else { contextPath = serverRequest.getRequestTarget(); } localAddress = serverRequest.getLocalAddress(); localName = serverRequest.getLocalHostname(); localPort = serverRequest.getLocalPort(); method = serverRequest.getMethod(); protocol = serverRequest.getProtocol(); remoteAddr = serverRequest.getRemoteAddress(); remoteHost = serverRequest.getRemoteHostname(); remotePort = serverRequest.getRemotePort(); serverName = serverRequest.getLocalHostname(); serverPort = serverRequest.getLocalPort(); webApplicationInputStream = new DefaultWebApplicationInputStream(); webApplicationInputStream.setWebApplicationRequest(this); webApplicationInputStream.setInputStream(wrapped.getInputStream()); Iterator headerNames = serverRequest.getHeaderNames(); while (headerNames.hasNext()) { String name = headerNames.next(); String value = serverRequest.getHeader(name); /* * If the request is passing in an Accept header we should honor it * and remove the default Accept header. */ if (name.equalsIgnoreCase("Accept")) { headerManager.removeHeader("Accept"); } serverRequest.getHeaders(name).forEachRemaining(x -> addHeader(name, x)); if (name.equalsIgnoreCase("Content-Type")) { setContentType(value); } if (name.equalsIgnoreCase("Content-Length")) { setContentLength(Integer.parseInt(value)); } if (name.equalsIgnoreCase("COOKIE")) { cookies = CookieParser.parse(value); Stream.of(cookies) .filter(x -> "JSESSIONID".equals(x.getName())) .findAny() .ifPresent(cookie -> { setRequestedSessionIdFromCookie(true); setRequestedSessionId(cookie.getValue()); }); } } if (contextPath != null) { String jsessionid = ";jsessionid="; int indexJsessionid = contextPath.indexOf(jsessionid); if (indexJsessionid > -1) { if (!isRequestedSessionIdFromCookie()) { setRequestedSessionIdFromURL(true); setRequestedSessionId(contextPath.substring(indexJsessionid + jsessionid.length())); } contextPath = contextPath.substring(0, indexJsessionid); } } if (serverRequest.isSecure()) { scheme = "https"; if (wrapped.getSslCipherSuite() != null) { setAttribute("jakarta.servlet.request.cipher_suite", wrapped.getSslCipherSuite()); } setAttribute("jakarta.servlet.request.key_size", wrapped.getSslKeySize()); if (wrapped.getSslCertificates() != null) { setAttribute("jakarta.servlet.request.X509Certificate", wrapped.getSslCertificates()); if (wrapped.getSslPrincipal() != null) { /* * setUserPrincipal(wrapped.getSslPrincipal()); * setAuthType(CLIENT_CERT_AUTH); */ } } } } } ================================================ FILE: http/webapp/src/main/java/cloud/piranha/http/webapp/HttpWebApplicationResponse.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.webapp; import cloud.piranha.http.api.HttpServerResponse; import cloud.piranha.core.impl.DefaultWebApplicationResponse; import java.io.IOException; import static java.lang.System.Logger.Level.WARNING; /** * The HttpServerResponse variant of WebApplicationResponse. * * @author Manfred Riem (mriem@manorrock.com) */ public class HttpWebApplicationResponse extends DefaultWebApplicationResponse { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(HttpWebApplicationResponse.class.getName()); /** * Stores the wrapped HttpServerResponse. */ protected final HttpServerResponse wrapped; /** * Constructor. * * @param wrapped the wrapped HttpServerResponse. */ public HttpWebApplicationResponse(HttpServerResponse wrapped) { this.wrapped = wrapped; setWebApplicationOutputStream(new HttpWebApplicationOutputStream(this, wrapped)); setResponseCloser(() -> { try { wrapped.closeResponse(); } catch (IOException ioe) { LOGGER.log(WARNING, () -> "IOException when flushing the underlying async output stream", ioe); } }); } } ================================================ FILE: http/webapp/src/main/java/cloud/piranha/http/webapp/HttpWebApplicationServer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.webapp; import cloud.piranha.http.api.HttpServerProcessor; import cloud.piranha.http.api.HttpServerProcessorEndState; import static cloud.piranha.http.api.HttpServerProcessorEndState.ASYNCED; import static cloud.piranha.http.api.HttpServerProcessorEndState.COMPLETED; import cloud.piranha.http.api.HttpServerRequest; import cloud.piranha.http.api.HttpServerResponse; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationRequest; import cloud.piranha.core.api.WebApplicationResponse; import cloud.piranha.core.api.WebApplicationServer; import cloud.piranha.core.api.WebApplicationServerRequestMapper; import static cloud.piranha.http.api.HttpServerProcessorEndState.UPGRADED; import jakarta.servlet.ServletException; import java.io.IOException; import java.lang.System.Logger; import static java.lang.System.Logger.Level.DEBUG; import static java.lang.System.Logger.Level.ERROR; import static java.lang.System.Logger.Level.TRACE; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * The default WebApplicationServer. * * @author Manfred Riem (mriem@manorrock.com) */ public class HttpWebApplicationServer implements HttpServerProcessor, WebApplicationServer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(HttpWebApplicationServer.class.getName()); /** * Stores the running boolean. */ protected boolean running = false; /** * Stores the request mapper. */ protected WebApplicationServerRequestMapper requestMapper; /** * Stores the web applications. */ protected final Map webApplications; /** * Constructor. */ public HttpWebApplicationServer() { this.requestMapper = new HttpWebApplicationServerRequestMapper(); this.webApplications = new ConcurrentHashMap<>(); } /** * Add a context path mapping. * * @param servletContextName the servlet context name. * @param contextPath the context path. */ public void addMapping(String servletContextName, String contextPath) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Adding context path: {0} for: {1}", contextPath, servletContextName); } for (WebApplication webApp : webApplications.values()) { if (webApp.getServletContextName().equals(servletContextName)) { requestMapper.addMapping(webApp, contextPath); break; } } } @Override public void addWebApplication(WebApplication webApplication) { if (LOGGER.isLoggable(DEBUG)) { LOGGER.log(DEBUG, () -> "Adding web application with context path: " + webApplication.getContextPath()); } webApplications.put(webApplication.getContextPath(), webApplication); requestMapper.addMapping(webApplication, webApplication.getContextPath()); } @Override public WebApplicationServerRequestMapper getRequestMapper() { return requestMapper; } @Override public void initialize() { if (LOGGER.isLoggable(DEBUG)) { LOGGER.log(DEBUG, "Starting initialization of {0} web application(s)", webApplications.size()); } for (WebApplication webApp : webApplications.values()) { ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(webApp.getClassLoader()); if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Initializing web application at: {0}", webApp.getContextPath()); } webApp.initialize(); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } if (LOGGER.isLoggable(DEBUG)) { LOGGER.log(DEBUG, "Finished initialization of {0} web application(s)", webApplications.size()); } } @Override public HttpServerProcessorEndState process(HttpServerRequest request, HttpServerResponse response) { HttpServerProcessorEndState state = COMPLETED; try { HttpWebApplicationRequest serverRequest = new HttpWebApplicationRequest(request); HttpWebApplicationResponse serverResponse = new HttpWebApplicationResponse(response); service(serverRequest, serverResponse); if (serverRequest.isAsyncStarted()) { state = ASYNCED; } if (serverRequest.isUpgraded()) { state = UPGRADED; } } catch (Throwable t) { LOGGER.log(ERROR, "An error occurred while processing the request", t); } if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Processor end state: {0}", state.name()); } return state; } /** * Service method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. * @throws ServletException when a servlet error occurs. */ @Override public void service(WebApplicationRequest request, WebApplicationResponse response) throws IOException, ServletException { String requestUri = request.getRequestURI(); if (requestUri == null) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Request URI is invalid"); } response.sendError(500); return; } if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Request URI: {0}", requestUri); } WebApplication webApplication = requestMapper.findMapping(requestUri); if (webApplication == null) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "No web application found for request URI: {0}", requestUri); } response.sendError(404); return; } ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(webApplication.getClassLoader()); String contextPath = webApplication.getContextPath(); request.setContextPath(contextPath); request.setServletPath(requestUri.substring(contextPath.length())); request.setWebApplication(webApplication); response.setWebApplication(webApplication); if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Context Path: {0}", contextPath); LOGGER.log(TRACE, "Servlet Path: {0}", request.getServletPath()); } webApplication.service(request, response); // Make sure the request is fully read wrt parameters (if any still) request.getParameterMap(); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } @Override public void setRequestMapper(WebApplicationServerRequestMapper requestMapper) { this.requestMapper = requestMapper; } @Override public void start() { if (!running) { LOGGER.log(DEBUG, "Starting HTTP web application server"); webApplications.values().forEach(webApp -> { ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(webApp.getClassLoader()); webApp.start(); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } }); LOGGER.log(DEBUG, "Started HTTP web application server"); running = true; } } @Override public void stop() { if (running) { LOGGER.log(DEBUG, "Stopping HTTP web application server"); webApplications.values().forEach(webApp -> { ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(webApp.getClassLoader()); webApp.stop(); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } }); LOGGER.log(DEBUG, "Stopped HTTP web application server"); running = false; } } } ================================================ FILE: http/webapp/src/main/java/cloud/piranha/http/webapp/HttpWebApplicationServerRequestMapper.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.webapp; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationServerRequestMapper; import java.lang.System.Logger; import static java.lang.System.Logger.Level.TRACE; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * The HTTP WebApplicationServerRequestMapper. * * @author Manfred Riem (mriem@manorrock.com) */ public class HttpWebApplicationServerRequestMapper implements WebApplicationServerRequestMapper { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(HttpWebApplicationServerRequestMapper.class.getName()); /** * Stores the web application mappings. */ private final ConcurrentHashMap mappings; /** * Constructor. */ public HttpWebApplicationServerRequestMapper() { this.mappings = new ConcurrentHashMap<>(); } /** * Add a mapping. * * @param webApplication the web application. * @param urlPatterns the url patterns to map (aka mappings). * @return the url patterns not added. */ @Override public Set addMapping(WebApplication webApplication, String... urlPatterns) { Set result = new HashSet<>(); for (String urlPattern : urlPatterns) { if (this.mappings.containsKey(urlPattern)) { result.add(urlPattern); } else { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Adding url pattern: {0}", urlPattern); } this.mappings.put(urlPattern, webApplication); } } return result; } /** * Find a mapping for the given path. * * @param path the path. * @return the mapping, or null if not found. */ @Override public WebApplication findMapping(String path) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Finding web application for: {0}", path); } WebApplication result = null; String mapping = findPrefixMatch(path); if (mapping != null) { result = this.mappings.get(mapping); } if (LOGGER.isLoggable(TRACE)) { if (result != null) { LOGGER.log(TRACE, "Found web application at: {0}", result.getContextPath()); } else { LOGGER.log(TRACE, "Unable to find web application for: {0}", path); } } return result; } /** * Find a mapping with the longest prefix mapping. * * @param path the path. * @return the mapping, or null if not found. */ private String findPrefixMatch(String path) { if (LOGGER.isLoggable(TRACE)) { LOGGER.log(TRACE, "Find prefix for: {0}", path); } String result = null; String found; for (;;) { found = findPrefixMatch(path, result); if (found != null) { result = found; } else { break; } } if (LOGGER.isLoggable(TRACE)) { if (result != null) { LOGGER.log(TRACE, "Found prefix: {0}", result); } else { LOGGER.log(TRACE, "Unable to find prefix for: {0}", path); } } return result; } /** * Find a mapping with a prefix mapping longer than the given current * prefix. * * @param path the path. * @param currentPrefix the current matched prefix. * @return the mapping, or null if not found. */ public String findPrefixMatch(String path, String currentPrefix) { String result = null; Enumeration prefixes = mappings.keys(); while (prefixes.hasMoreElements()) { String prefix = prefixes.nextElement(); if (path.startsWith(prefix)) { if (result == null) { result = prefix; } break; } } if (result != null && currentPrefix != null && result.length() <= currentPrefix.length()) { result = null; } return result; } } ================================================ FILE: http/webapp/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the bridge between the HttpServer API and the * WebApplication API. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.http.webapp { exports cloud.piranha.http.webapp; opens cloud.piranha.http.webapp; requires transitive cloud.piranha.core.api; requires transitive cloud.piranha.core.impl; requires transitive cloud.piranha.http.api; } ================================================ FILE: http/webapp/src/test/java/cloud/piranha/http/webapp/HttpWebApplicationRequestTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.webapp; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.impl.DefaultHttpServer; import jakarta.servlet.ServletRegistration.Dynamic; import java.io.IOException; import java.net.CookieManager; import java.net.HttpCookie; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * The JUnit tests for HttpWebApplicationRequest. * * @author Manfred Riem (mriem@manorrock.com) */ class HttpWebApplicationRequestTest { /** * Stores the HTTP server. */ private HttpServer httpServer; /** * Stores the HTTP web application server. */ private HttpWebApplicationServer server; /** * After each. */ @AfterEach void afterEach() { server.stop(); httpServer.stop(); } /** * Before each. */ @BeforeEach void beforeEach() { server = new HttpWebApplicationServer(); DefaultWebApplication application = new DefaultWebApplication(); application.setContextPath(""); Dynamic registration = application.addServlet("snoop", new TestSnoopServlet()); registration.setAsyncSupported(true); application.addServletMapping("snoop", "/Snoop", "/Snoop/*"); server.addWebApplication(application); server.initialize(); server.start(); httpServer = new DefaultHttpServer(-2, server, false); httpServer.start(); } /** * Test getGetAuthType method. * * @throws Exception when a serious error occurs. */ @Test void testGetAuthType() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getAuthType")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("null", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getCharacterEncoding method. * * @throws Exception when a serious error occurs. */ @Test void testGetCharacterEncoding() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getCharacterEncoding")) .header("Content-Type", "text/html; charset=ISO-8859-1") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("ISO-8859-1", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getCharacterEncoding method. * * @throws Exception when a serious error occurs. */ @Test void testGetCharacterEncoding2() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getCharacterEncoding")) .header("Content-Type", "text/html; charset=UTF-8") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("UTF-8", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getContentLength method. * * @throws Exception when a serious error occurs. */ @Test void testGetContentLength() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getContentLength")) .POST(HttpRequest.BodyPublishers.ofString("")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertTrue(Integer.parseInt(response.body().trim()) >= 0); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getContentType method. * * @throws Exception when a serious error occurs. */ @Test void testGetContentType() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getContentType")) .header("Content-Type", "application/json") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("application/json", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getContextPath method. * * @throws Exception when a serious error occurs. */ @Test void testGetContextPath() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getContextPath")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getCookies method. * * @throws Exception when a serious error occurs. */ @Test void testGetCookies() throws Exception { try { CookieManager cookieManager = new CookieManager(); HttpCookie cookie = new HttpCookie("name", "value"); cookie.setPath("/"); cookie.setVersion(0); cookieManager.getCookieStore().add( URI.create("http://localhost:" + httpServer.getServerPort() + "/Snoop"), cookie); HttpClient client = HttpClient .newBuilder() .cookieHandler(cookieManager) .build(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getCookies")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("value", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getDateHeader method. * * @throws Exception when a serious error occurs. */ @Test void testGetDateHeader() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getDateHeader")) .header("MY_DATE", "Wed, 16 Oct 2019 07:28:00 GMT") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertTrue(response.body().contains("1571210880000")); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getHeader method. * * @throws Exception when a serious error occurs. */ @Test void testGetHeader() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getHeader")) .header("MY_HEADER", "1234") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("1234", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getHeaderNames method. * * @throws Exception when a serious error occurs. */ @Test void testGetHeaderNames() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getHeaderNames")) .header("MY_HEADER", "1234") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertTrue(response.body().contains("MY_HEADER")); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getIntHeader method. * * @throws Exception when a serious error occurs. */ @Test void testGetIntHeader() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getIntHeader")) .header("MY_INT", "1234") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertTrue(response.body().contains("1234")); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getLocalAddr method. * * @throws Exception when a serious error occurs. */ @Test void testGetLocalAddr() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getLocalAddr")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertTrue(response.body().contains("127.0.0.1")); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getLocalName method. * * @throws Exception when a serious error occurs. */ @Test void testGetLocalName() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getLocalName")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertNotEquals("", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getLocalPort method. * * @throws Exception when a serious error occurs. */ @Test void testGetLocalPort() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getLocalPort")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertNotEquals(-1, Integer.parseInt(response.body().trim())); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getLocale method. * * @throws Exception when a serious error occurs. */ @Test void testGetLocale() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getLocale")) .header("Accept-Language", "nl_nl") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("nl_nl", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getLocales method. * * @throws Exception when a serious error occurs. */ @Test void testGetLocales() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getLocales")) .header("Accept-Language", "nl_nl, en_us") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("nl_nl,en_us,", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getMethod method. * * @throws Exception when a serious error occurs. */ @Test void testGetMethod() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getMethod")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertTrue(response.body().contains("GET")); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getParameter method. * * @throws Exception when a serious error occurs. */ @Test void testGetParameter() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getParameter¶meter=value")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("value", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getParts method. * * @throws Exception when a serious error occurs. */ @Test void testGetParts() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getParts")) .header("Content-Type", "multipart/form-data") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("true", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getPathInfo method. * * @throws Exception when a serious error occurs. */ @Test void testGetPathInfo() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop/pathInfo?getPathInfo")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("/pathInfo", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getPathTranslated method. * * @throws Exception when a serious error occurs. */ @Test void testGetPathTranslated() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop/pathTranslated?getPathTranslated")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("null", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getProtocol method. * * @throws Exception when a serious error occurs. */ @Test void testGetProtocol() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getProtocol")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("HTTP/1.1", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getQueryString method. * * @throws Exception when a serious error occurs. */ @Test void testGetQueryString() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getQueryString")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertTrue(response.body().contains("getQueryString")); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getRemoteAddr method. * * @throws Exception when a serious error occurs. */ @Test void testGetRemoteAddr() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getRemoteAddr")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertTrue(response.body().contains("127.0.0.1")); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getRemoteHost method. * * @throws Exception when a serious error occurs. */ @Test void testGetRemoteHost() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getRemoteHost")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertNotEquals("", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getRemotePort method. * * @throws Exception when a serious error occurs. */ @Test void testGetRemotePort() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getRemotePort")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertTrue(Integer.parseInt(response.body().trim()) > 0); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getRemoteUser method. * * @throws Exception when a serious error occurs. */ @Test void testGetRemoteUser() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getRemoteUser")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("null", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getRequestURI method. * * @throws Exception when a serious error occurs. */ @Test void testGetRequestURI() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getRequestURI")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("/Snoop", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getRequestURL method. * * @throws Exception when a serious error occurs. */ @Test void testGetRequestURL() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getRequestURL")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertTrue(response.body().contains("http://")); assertTrue(response.body().contains(Integer.toString(httpServer.getServerPort()))); assertTrue(response.body().contains("/Snoop")); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getRequestedSessionId method. * * @throws Exception when a serious error occurs. */ @Test void testGetRequestedSessionId() throws Exception { try { CookieManager cookieManager = new CookieManager(); HttpCookie cookie = new HttpCookie("JSESSIONID", "requestedSessionId"); cookie.setPath("/"); cookie.setVersion(0); cookieManager.getCookieStore().add( URI.create("http://localhost:" + httpServer.getServerPort() + "/Snoop"), cookie); HttpClient client = HttpClient .newBuilder() .cookieHandler(cookieManager) .build(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getRequestedSessionId")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("requestedSessionId", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getScheme method. * * @throws Exception when a serious error occurs. */ @Test void testGetScheme() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getScheme")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("http", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getServerName method. * * @throws Exception when a serious error occurs. */ @Test void testGetServerName() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getServerName")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertNotEquals("", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getServerPort method. * * @throws Exception when a serious error occurs. */ @Test void testGetServerPort() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getServerPort")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertTrue(response.body().contains(Integer.toString(httpServer.getServerPort()))); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getServletPath method. * * @throws Exception when a serious error occurs. */ @Test void testGetServletPath() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getServletPath")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("/Snoop", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test getSession method. * * @throws Exception when a serious error occurs. */ @Test void testGetSession() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?getSession")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertTrue(Long.parseLong(response.body().trim()) > 0); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test isAsyncStarted method. * * @throws Exception when a serious error occurs. */ @Test void testIsAsyncStarted() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?isAsyncStarted")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("false", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test isAsyncSupported method. * * @throws Exception when a serious error occurs. */ @Test void testIsAsyncSupported() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?isAsyncSupported")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("true", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test isRequestedSessionIdFromCookie method. * * @throws Exception when a serious error occurs. */ @Test void testIsRequestedSessionIdFromCookie() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?isRequestedSessionIdFromCookie")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("false", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test isRequestedSessionIdFromURL method. * * @throws Exception when a serious error occurs. */ @Test void testIsRequestedSessionIdFromURL() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?isRequestedSessionIdFromURL")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("false", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test isRequestedSessionIdValid method. * * @throws Exception when a serious error occurs. */ @Test void testIsRequestedSessionIdValid() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?isRequestedSessionIdValid")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("false", response.body().trim()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test isSecure method. * * @throws Exception when a serious error occurs. */ @Test void testIsSecure() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/Snoop?isSecure")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertTrue(response.body().contains("false")); } catch (IOException ioe) { throw new RuntimeException(ioe); } } } ================================================ FILE: http/webapp/src/test/java/cloud/piranha/http/webapp/HttpWebApplicationResponseTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.webapp; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.impl.DefaultHttpServer; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRegistration.Dynamic; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.Locale; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * The JUnit tests for HttpWebApplicationRequest. * * @author Manfred Riem (mriem@manorrock.com) */ class HttpWebApplicationResponseTest { /** * Stores the HTTP server. */ private HttpServer httpServer; /** * Stores the HTTP web application server. */ private HttpWebApplicationServer server; /** * After each. */ @AfterEach void afterEach() { server.stop(); httpServer.stop(); } /** * Before each. */ @BeforeEach void beforeEach() { server = new HttpWebApplicationServer(); DefaultWebApplication application = new DefaultWebApplication(); application.getManager().getLocaleEncodingManager().addCharacterEncoding("ja", "Shift_Jis"); application.setContextPath(""); Dynamic registration = application.addServlet("ResponseServlet", new HttpServlet() { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { switch (request.getQueryString()) { case "addIntHeader" -> addIntHeader(request, response); case "setCharacterEncoding" -> setCharacterEncoding(request, response); case "setLocale" -> setLocale(request, response); default -> { } } } private void addIntHeader(HttpServletRequest request, HttpServletResponse response) throws IOException { response.addIntHeader("name", 1234); response.addIntHeader("name", 2345); response.flushBuffer(); } private void setLocale(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType("text/html"); response.setLocale(new Locale("ja")); response.flushBuffer(); } private void setCharacterEncoding(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType("text/html"); response.setCharacterEncoding("ISO-8859-7"); response.flushBuffer(); } }); registration.setAsyncSupported(true); application.addServletMapping("ResponseServlet", "/response"); server.addWebApplication(application); server.initialize(); server.start(); httpServer = new DefaultHttpServer(-2, server, false); httpServer.start(); } /** * Test setAddIntHeader method. * * @throws Exception when a serious error occurs. */ @Test void testAddIntHeader() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/response?addIntHeader")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("1234, 2345", response.headers().firstValue("name").get()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test setLocale method. * * @throws Exception when a serious error occurs. */ @Test void testSetLocale() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/response?setLocale")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("text/html;charset=Shift_Jis", response.headers().firstValue("Content-Type").get()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * Test setCharacterEncoding method. * * @throws Exception when a serious error occurs. */ @Test void testSetCharacterEncoding() throws Exception { try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpServer.getServerPort() + "/response?setCharacterEncoding")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("text/html;charset=ISO-8859-7", response.headers().firstValue("Content-Type").get()); } catch (IOException ioe) { throw new RuntimeException(ioe); } } } ================================================ FILE: http/webapp/src/test/java/cloud/piranha/http/webapp/HttpWebApplicationServerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.webapp; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.http.impl.DefaultHttpServer; import cloud.piranha.http.api.HttpServer; import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the HttpWebApplicationServer class. * * @author Manfred Riem (mriem@manorrock.com) */ class HttpWebApplicationServerTest { /** * Test addMapping method. */ @Test void testAddMapping() { HttpWebApplicationServer server = new HttpWebApplicationServer(); DefaultWebApplication webApp = new DefaultWebApplication(); webApp.setServletContextName("mycontext"); server.addWebApplication(webApp); server.addMapping("notthere", "notreal"); assertEquals(webApp, server.getRequestMapper().findMapping("/notreal")); } /** * Test addMapping method. */ @Test void testAddMapping2() { HttpWebApplicationServer server = new HttpWebApplicationServer(); DefaultWebApplication webApp = new DefaultWebApplication(); webApp.setServletContextName("mycontext"); server.addWebApplication(webApp); server.addMapping("mycontext", "mycontextmapping"); assertEquals(webApp, server.getRequestMapper().findMapping("/mycontextmapping")); } /** * Test addMapping method. */ @Test void testAddMapping3() { HttpWebApplicationServer server = new HttpWebApplicationServer(); DefaultWebApplication webApp = new DefaultWebApplication(); webApp.setServletContextName("mycontext"); server.addWebApplication(webApp); server.addMapping("mycontext", "myurlpattern"); server.addMapping("mycontext", "myurlpattern"); assertEquals(webApp, server.getRequestMapper().findMapping("/myurlpattern")); } /** * Test getRequestMapper method. */ @Test void testGetRequestMapper() { HttpWebApplicationServer server = new HttpWebApplicationServer(); server.setRequestMapper(new HttpWebApplicationServerRequestMapper()); assertNotNull(server.getRequestMapper()); } /** * Test process method. * * @throws Exception when a serious error occurs. */ @Test void testProcess() throws Exception { HttpWebApplicationServer server = new HttpWebApplicationServer(); HttpServer httpServer = new DefaultHttpServer(8180, server, false); DefaultWebApplication application = new DefaultWebApplication(); application.setContextPath("/context"); application.addServlet("snoop", new TestSnoopServlet()); application.addServletMapping("snoop", "/snoop/*"); server.addWebApplication(application); server.initialize(); server.start(); httpServer.start(); try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder(new URI("http://localhost:8180/context/snoop/index.html")).build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.discarding()); assertEquals(200, response.statusCode()); } catch (IOException ioe) { throw new RuntimeException(ioe); } httpServer.stop(); server.stop(); } @Test void testSessionUrlRewriting() throws Exception { HttpWebApplicationServer server = new HttpWebApplicationServer(); HttpServer httpServer = new DefaultHttpServer(8181, server, false); DefaultWebApplication application = new DefaultWebApplication(); application.setContextPath("/context"); application.addServlet("snoop", new TestSnoopServlet()); application.addServletMapping("snoop", "/snoop/*"); server.addWebApplication(application); server.initialize(); server.start(); httpServer.start(); try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder(new URI("http://localhost:8181/context/snoop/index.html;jsessionid=customsessionid")).build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertTrue(response.body().contains("customsessionid")); } catch (IOException ioe) { throw new RuntimeException(ioe); } httpServer.stop(); server.stop(); } @Test void testSessionUrlRewriting2() throws Exception { HttpWebApplicationServer server = new HttpWebApplicationServer(); HttpServer httpServer = new DefaultHttpServer(8182, server, false); DefaultWebApplication application = new DefaultWebApplication(); application.setContextPath("/context"); application.addServlet("snoop", new TestSnoopServlet()); application.addServletMapping("snoop", "/snoop/*"); server.addWebApplication(application); server.initialize(); server.start(); httpServer.start(); try { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder(new URI("http://localhost:8182/context/snoop/index.html;jsessionid=sessionIdURL")) .headers("Cookie", "JSESSIONID=sessionIdCookie") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertTrue(response.body().contains("sessionIdCookie")); } catch (IOException ioe) { throw new RuntimeException(ioe); } httpServer.stop(); server.stop(); } } ================================================ FILE: http/webapp/src/test/java/cloud/piranha/http/webapp/TestSnoopServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.http.webapp; import java.io.IOException; import java.io.PrintWriter; import java.util.Arrays; import java.util.Enumeration; import jakarta.servlet.ServletException; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.security.Principal; /** * A test Snoop Servlet. * * @author Manfred Riem (mriem@manorrock.com) */ public class TestSnoopServlet extends HttpServlet { /** * Stores the serial version UID. */ private static final long serialVersionUID = 1L; /** * Processes the request. * * @param request the servlet request. * @param response the servlet response. * @throws IOException when an I/O error occurs. * @throws ServletException when a servlet error occurs. */ protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if (request.getQueryString() == null) { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); out.println(""); out.println(" "); out.println(" Snoop"); out.println(" "); out.println(" "); out.println("

Snoop

"); out.println(" "); out.println(" "); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println("
Attribute Names:" + request.getAttributeNames() + "
Dispatcher Type:" + request.getDispatcherType() + "
Parameter Map:" + request.getParameterMap() + "
Parameter Names:" + request.getParameterNames() + "
Requested Session Id:" + request.getRequestedSessionId() + "
Is Requested Session Id From Cookie:" + request.isRequestedSessionIdFromCookie() + "
Is Requested Session Id From URL:" + request.isRequestedSessionIdFromURL() + "
"); out.println("Attributes"); Enumeration attributeNames = request.getAttributeNames(); out.println(""); while (attributeNames.hasMoreElements()) { String name = attributeNames.nextElement(); out.println(""); } out.println("
" + name + "" + request.getAttribute(name) + "
"); out.println("Parameters"); Enumeration parameterNames = request.getParameterNames(); out.println(""); while (parameterNames.hasMoreElements()) { String name = parameterNames.nextElement(); out.println(""); } out.println("
" + name + "" + Arrays.toString(request.getParameterValues(name)) + "
"); out.println(""); out.println(""); } else { switch (request.getQueryString()) { case "getAuthType" -> testGetAuthType(request, response); case "getCharacterEncoding" -> testGetCharacterEncoding(request, response); case "getContentLength" -> testGetContentLength(request, response); case "getContentType" -> testGetContentType(request, response); case "getContextPath" -> testGetContextPath(request, response); case "getCookies" -> testGetCookies(request, response); case "getDateHeader" -> testGetDateHeader(request, response); case "getHeader" -> testGetHeader(request, response); case "getHeaderNames" -> testGetHeaderNames(request, response); case "getIntHeader" -> testGetIntHeader(request, response); case "getLocalAddr" -> testGetLocalAddr(request, response); case "getLocalName" -> testGetLocalName(request, response); case "getLocalPort" -> testGetLocalPort(request, response); case "getLocale" -> testGetLocale(request, response); case "getLocales" -> testGetLocales(request, response); case "getMethod" -> testGetMethod(request, response); case "getParts" -> testGetParts(request, response); case "getPathInfo" -> testGetPathInfo(request, response); case "getPathTranslated" -> testGetPathTranslated(request, response); case "getProtocol" -> testGetProtocol(request, response); case "getQueryString" -> testGetQueryString(request, response); case "getRemoteAddr" -> testGetRemoteAddr(request, response); case "getRemoteHost" -> testGetRemoteHost(request, response); case "getRemotePort" -> testGetRemotePort(request, response); case "getRemoteUser" -> testGetRemoteUser(request, response); case "getRequestedSessionId" -> testGetRequestedSessionId(request, response); case "getRequestURI" -> testGetRequestURI(request, response); case "getRequestURL" -> testGetRequestURL(request, response); case "getScheme" -> testGetScheme(request, response); case "getServerName" -> testGetServerName(request, response); case "getServerPort" -> testGetServerPort(request, response); case "getServletPath" -> testGetServletPath(request, response); case "getSession" -> testGetSession(request, response); case "getUserPrincipal" -> testGetUserPrincipal(request, response); case "isAsyncStarted" -> testIsAsyncStarted(request, response); case "isAsyncSupported" -> testIsAsyncSupported(request, response); case "isRequestedSessionIdFromCookie" -> testIsRequestedSessionIdFromCookie(request, response); case "isRequestedSessionIdFromURL" -> testIsRequestedSessionIdFromURL(request, response); case "isRequestedSessionIdValid" -> testIsRequestedSessionIdValid(request, response); case "isSecure" -> testIsSecure(request, response); } if (request.getQueryString().contains("getParameter")) { testGetParameter(request, response); } } } /** * Handles the GET request. * * @param request the servlet request. * @param response the servlet response. * @throws IOException when an I/O error occurs * @throws ServletException when a servlet error occurs */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { processRequest(request, response); } /** * Handles the POST request. * * @param request the servlet request. * @param response the servlet response. * @throws IOException when an I/O error occurs. * @throws ServletException when a servlet error occurs. */ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { processRequest(request, response); } /** * Test getAuthType method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetAuthType(HttpServletRequest request, HttpServletResponse response) throws IOException { String authType = request.getAuthType(); PrintWriter writer = response.getWriter(); writer.println(authType); } /** * Test getCharacterEncoding method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetCharacterEncoding(HttpServletRequest request, HttpServletResponse response) throws IOException { String characterEncoding = request.getCharacterEncoding(); PrintWriter writer = response.getWriter(); writer.println(characterEncoding); } /** * Test getContentLength method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetContentLength(HttpServletRequest request, HttpServletResponse response) throws IOException { int contentLength = request.getContentLength(); PrintWriter writer = response.getWriter(); writer.println(contentLength); } /** * Test getContentType method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetContentType(HttpServletRequest request, HttpServletResponse response) throws IOException { String contentType = request.getContentType(); PrintWriter writer = response.getWriter(); writer.println(contentType); } /** * Test getContextPath method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetContextPath(HttpServletRequest request, HttpServletResponse response) throws IOException { String contextPath = request.getContextPath(); PrintWriter writer = response.getWriter(); writer.println(contextPath); } /** * Test getCookies method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetCookies(HttpServletRequest request, HttpServletResponse response) throws IOException { Cookie cookie = request.getCookies()[0]; PrintWriter writer = response.getWriter(); writer.println(cookie.getValue()); } /** * Test getDateHeader method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetDateHeader(HttpServletRequest request, HttpServletResponse response) throws IOException { long dateHeader = request.getDateHeader("MY_DATE"); PrintWriter writer = response.getWriter(); writer.println(dateHeader); } /** * Test getHeader method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetHeader(HttpServletRequest request, HttpServletResponse response) throws IOException { String value = request.getHeader("MY_HEADER"); PrintWriter writer = response.getWriter(); writer.println(value); } /** * Test getHeaderNames method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetHeaderNames(HttpServletRequest request, HttpServletResponse response) throws IOException { StringBuilder builder = new StringBuilder(); request.getHeaderNames().asIterator().forEachRemaining(header -> builder.append(header)); PrintWriter writer = response.getWriter(); writer.println(builder.toString()); } /** * Test getIntHeader method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetIntHeader(HttpServletRequest request, HttpServletResponse response) throws IOException { int intHeader = request.getIntHeader("MY_INT"); PrintWriter writer = response.getWriter(); writer.println(intHeader); } /** * Test getLocalAddr method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetLocalAddr(HttpServletRequest request, HttpServletResponse response) throws IOException { String localAddr = request.getLocalAddr(); PrintWriter writer = response.getWriter(); writer.println(localAddr); } /** * Test getLocalName method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetLocalName(HttpServletRequest request, HttpServletResponse response) throws IOException { String localName = request.getLocalName(); PrintWriter writer = response.getWriter(); writer.println(localName); } /** * Test getLocalPort method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetLocalPort(HttpServletRequest request, HttpServletResponse response) throws IOException { int localPort = request.getLocalPort(); PrintWriter writer = response.getWriter(); writer.println(localPort); } /** * Test getLocale method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetLocale(HttpServletRequest request, HttpServletResponse response) throws IOException { String locale = request.getLocale().toString(); PrintWriter writer = response.getWriter(); writer.println(locale); } /** * Test getLocales method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetLocales(HttpServletRequest request, HttpServletResponse response) throws IOException { StringBuilder locales = new StringBuilder(); request.getLocales().asIterator().forEachRemaining(locale -> locales.append(locale).append(",")); PrintWriter writer = response.getWriter(); writer.println(locales.toString()); } /** * Test getMethod method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetMethod(HttpServletRequest request, HttpServletResponse response) throws IOException { String method = request.getMethod(); PrintWriter writer = response.getWriter(); writer.println(method); } /** * Test getParameter method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetParameter(HttpServletRequest request, HttpServletResponse response) throws IOException { String value = request.getParameter("parameter"); PrintWriter writer = response.getWriter(); writer.println(value); } /** * Test getParts method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetParts(HttpServletRequest request, HttpServletResponse response) throws IOException { PrintWriter writer = response.getWriter(); try { String noParts = Boolean.toString(request.getParts().isEmpty()); writer.println(noParts); } catch(ServletException se) { writer.println("FAILED"); } } /** * Test getPathInfo method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetPathInfo(HttpServletRequest request, HttpServletResponse response) throws IOException { String pathInfo = request.getPathInfo(); PrintWriter writer = response.getWriter(); writer.println(pathInfo); } /** * Test getPathTranslated method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetPathTranslated(HttpServletRequest request, HttpServletResponse response) throws IOException { String pathTranslated = request.getPathTranslated(); PrintWriter writer = response.getWriter(); writer.println(pathTranslated); } /** * Test getProtocol method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetProtocol(HttpServletRequest request, HttpServletResponse response) throws IOException { String protocol = request.getProtocol(); PrintWriter writer = response.getWriter(); writer.println(protocol); } /** * Test getQueryString method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetQueryString(HttpServletRequest request, HttpServletResponse response) throws IOException { String queryString = request.getQueryString(); PrintWriter writer = response.getWriter(); writer.println(queryString); } /** * Test getRemoteAddr method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetRemoteAddr(HttpServletRequest request, HttpServletResponse response) throws IOException { String remoteAddr = request.getRemoteAddr(); PrintWriter writer = response.getWriter(); writer.println(remoteAddr); } /** * Test getRemoteHost method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetRemoteHost(HttpServletRequest request, HttpServletResponse response) throws IOException { String remoteHost = request.getRemoteHost(); PrintWriter writer = response.getWriter(); writer.println(remoteHost); } /** * Test getRemotePort method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetRemotePort(HttpServletRequest request, HttpServletResponse response) throws IOException { int remotePort = request.getRemotePort(); PrintWriter writer = response.getWriter(); writer.println(remotePort); } /** * Test getRemoteUser method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetRemoteUser(HttpServletRequest request, HttpServletResponse response) throws IOException { String remoteUser = request.getRemoteUser(); PrintWriter writer = response.getWriter(); writer.println(remoteUser); } /** * Test getRequestURI method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetRequestURI(HttpServletRequest request, HttpServletResponse response) throws IOException { String requestURI = request.getRequestURI(); PrintWriter writer = response.getWriter(); writer.println(requestURI); } /** * Test getRequestURL method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetRequestURL(HttpServletRequest request, HttpServletResponse response) throws IOException { String requestURL = request.getRequestURL().toString(); PrintWriter writer = response.getWriter(); writer.println(requestURL); } /** * Test getRequestedSessionId method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetRequestedSessionId(HttpServletRequest request, HttpServletResponse response) throws IOException { String requestedSessionId = request.getRequestedSessionId(); PrintWriter writer = response.getWriter(); writer.println(requestedSessionId); } /** * Test getScheme method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetScheme(HttpServletRequest request, HttpServletResponse response) throws IOException { String scheme = request.getScheme(); PrintWriter writer = response.getWriter(); writer.println(scheme); } /** * Test getServerName method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetServerName(HttpServletRequest request, HttpServletResponse response) throws IOException { String serverName = request.getServerName(); PrintWriter writer = response.getWriter(); writer.println(serverName); } /** * Test getServerPort method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetServerPort(HttpServletRequest request, HttpServletResponse response) throws IOException { int serverPort = request.getServerPort(); PrintWriter writer = response.getWriter(); writer.println(serverPort); } /** * Test getServletPath method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetServletPath(HttpServletRequest request, HttpServletResponse response) throws IOException { String servletPath = request.getServletPath(); PrintWriter writer = response.getWriter(); writer.println(servletPath); } /** * Test getSession method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetSession(HttpServletRequest request, HttpServletResponse response) throws IOException { Long creationTime = request.getSession().getCreationTime(); PrintWriter writer = response.getWriter(); writer.println(creationTime); } /** * Test getUserPrincipal method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testGetUserPrincipal(HttpServletRequest request, HttpServletResponse response) throws IOException { Principal userPrincipal = request.getUserPrincipal(); PrintWriter writer = response.getWriter(); writer.println(userPrincipal); } /** * Test isAsyncStarted method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testIsAsyncStarted(HttpServletRequest request, HttpServletResponse response) throws IOException { boolean isAsyncStarted = request.isAsyncStarted(); PrintWriter writer = response.getWriter(); writer.println(isAsyncStarted); } /** * Test isAsyncSupported method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testIsAsyncSupported(HttpServletRequest request, HttpServletResponse response) throws IOException { boolean isAsyncSupported = request.isAsyncSupported(); PrintWriter writer = response.getWriter(); writer.println(isAsyncSupported); } /** * Test isRequestedSessionIdFromCookie method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testIsRequestedSessionIdFromCookie(HttpServletRequest request, HttpServletResponse response) throws IOException { boolean isRequestedSessionIdFromCookie = request.isRequestedSessionIdFromCookie(); PrintWriter writer = response.getWriter(); writer.println(isRequestedSessionIdFromCookie); } /** * Test isRequestedSessionIdFromURL method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testIsRequestedSessionIdFromURL(HttpServletRequest request, HttpServletResponse response) throws IOException { boolean isRequestedSessionIdFromURL = request.isRequestedSessionIdFromURL(); PrintWriter writer = response.getWriter(); writer.println(isRequestedSessionIdFromURL); } /** * Test isRequestedSessionIdValid method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testIsRequestedSessionIdValid(HttpServletRequest request, HttpServletResponse response) throws IOException { boolean isRequestedSessionIdValid = request.isRequestedSessionIdValid(); PrintWriter writer = response.getWriter(); writer.println(isRequestedSessionIdValid); } /** * Test isSecure method. * * @param request the request. * @param response the response. * @throws IOException when an I/O error occurs. */ private void testIsSecure(HttpServletRequest request, HttpServletResponse response) throws IOException { boolean isSecure = request.isSecure(); PrintWriter writer = response.getWriter(); writer.println(isSecure); } } ================================================ FILE: maven/archetypes/pom.xml ================================================ 4.0.0 cloud.piranha.maven project 25.4.0-SNAPSHOT cloud.piranha.maven.archetypes project pom Piranha - Maven - Archetypes default file:///tmp/piranha/maven/archetypes/ ================================================ FILE: maven/plugin/pom.xml ================================================ 4.0.0 cloud.piranha.maven project 25.4.0-SNAPSHOT piranha-maven-plugin maven-plugin Piranha - Maven - Plugin The Piranha Maven Plugin delivers you with a Maven plugin you can use to deploy your web application to any of our Piranha distributions facilitating an inner-loop development experience. UTF-8 org.apache.maven.plugin-tools maven-plugin-tools-annotations provided org.apache.maven maven-artifact org.apache.maven.plugins maven-checkstyle-plugin validate validate check false false **/HelpMojo.java,module-info.java org.apache.maven.plugins maven-plugin-plugin piranha true mojo-descriptor descriptor help-goal helpmojo default file:///tmp/piranha/maven/plugin/ org.apache.maven.plugins maven-plugin-report-plugin org.apache.maven.plugins maven-project-info-reports-plugin help index ================================================ FILE: maven/plugin/src/main/java/cloud/piranha/maven/plugin/BaseMojo.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.maven.plugin; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import java.util.zip.ZipFile; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugins.annotations.Parameter; /** * The base Mojo for the start and run goals. * * @author Manfred Riem (mriem@manorrock.com) */ public abstract class BaseMojo extends AbstractMojo { /** * Stores the 'Unable to create directories' message. */ protected static final String UNABLE_TO_CREATE_DIRECTORIES = "Unable to create directories"; /** * Stores the build directory. */ @Parameter(defaultValue = "${project.build.directory}", required = true, readonly = true) protected String buildDirectory; /** * Stores the distribution to use. */ @Parameter(defaultValue = "coreprofile", property = "piranha.distribution", required = false) protected String distribution; /** * Stores the context path. */ @Parameter(required = false, property = "piranha.contextPath") protected String contextPath; /** * Stores the HTTP port. */ @Parameter(defaultValue = "8080", property = "piranha.httpPort", required = false) protected Integer httpPort; /** * Stores the HTTPS keystore file. */ @Parameter(property = "piranha.httpsKeystoreFile", required = false) protected String httpsKeystoreFile; /** * Stores the HTTPS keystore password. */ @Parameter(property = "piranha.httpsKeystorePassword", required = false) protected String httpsKeystorePassword; /** * Stores the HTTPS port */ @Parameter(defaultValue = "-1", property = "piranha.httpsPort", required = false) protected Integer httpsPort; /** * Stores the JVM arguments. */ @Parameter(required = false, property = "piranha.jvmArguments") protected String jvmArguments; /** * Stores the local repository directory. */ protected File localRepositoryDirectory = new File(System.getProperty("user.home"), ".m2/repository"); /** * Stores the Piranha JAR/Zip file. */ protected File piranhaFile; /** * Stores the Piranha type (jar or zip). */ protected String piranhaType = "jar"; /** * Stores the runtime directory. */ @Parameter(defaultValue = "${project.build.directory}/piranha", property = "piranha.runtimeDirectory", required = true) protected String runtimeDirectory; /** * Stores the skip property. */ @Parameter(defaultValue= "false", property="piranha.skip") protected boolean skip; /** * Stores the version of the Piranha runtime to use. */ @Parameter(property = "piranha.version", required = false) protected String version; /** * Stores the WAR name. */ @Parameter(defaultValue = "${project.build.finalName}", property="piranha.warName", required = true, readonly = true) protected String warName; /** * Constructor. */ public BaseMojo() { } /** * Convert a Maven groupId to a path snippet. * * @param groupId the groupId. * @return the path. */ protected String convertGroupIdToPath(String groupId) { return groupId.replace('.', '/'); } /** * Create an artifact path from a groupId, artifactId, version and type. * * @param groupId the groupId. * @param artifactId the artifactId. * @param version the version * @param type the type. * @return the artifact path. */ protected String createArtifactPath( String groupId, String artifactId, String version, String type) { String artifactPathFormat = "%s/%s/%s/%s-%s.%s"; return String.format(artifactPathFormat, convertGroupIdToPath(groupId), artifactId, version, artifactId, version, type.toLowerCase()); } /** * Create the Maven central artifact URL * * @param groupId the groupId. * @param artifactId the artifactId. * @param version the version * @param type the type. * @return the URL. * @throws IOException when an I/O error occurs. */ @SuppressWarnings("deprecation") protected URL createMavenCentralArtifactUrl(String groupId, String artifactId, String version, String type) throws IOException { return new URL("https://repo1.maven.org/maven2/" + createArtifactPath(groupId, artifactId, version, type)); } /** * Determine what version of Piranha to use. */ protected void determineVersionToUse() { if (version == null) { version = getClass().getPackage().getImplementationVersion(); } } /** * Copy the WAR file. * * @throws IOException when an I/O error occurs. */ protected void copyWarFile() throws IOException { File warFile = new File(buildDirectory, warName + ".war"); File outputFile; if (piranhaType.equals("jar")) { outputFile = new File(runtimeDirectory, warName + ".war"); } else { outputFile = new File(runtimeDirectory + File.separator + "webapps", warName + ".war"); } if (!outputFile.getParentFile().exists()) { outputFile.getParentFile().mkdirs(); } Files.copy(warFile.toPath(), outputFile.toPath(), REPLACE_EXISTING); } /** * Download the Piranha distribution. * * @throws IOException when an I/O error occurs. */ protected void downloadDistribution() throws IOException { if (distribution.equals("server")) { piranhaType = "zip"; } URL downloadUrl = createMavenCentralArtifactUrl( "cloud.piranha.dist", "piranha-dist-" + distribution, version, piranhaType ); String artifactPath = createArtifactPath( "cloud.piranha.dist", "piranha-dist-" + distribution, version, piranhaType ); File file = new File(localRepositoryDirectory, artifactPath); if (!file.exists() && !file.getParentFile().mkdirs()) { System.err.println(UNABLE_TO_CREATE_DIRECTORIES); } try ( InputStream inputStream = downloadUrl.openStream()) { Files.copy(inputStream, file.toPath(), StandardCopyOption.REPLACE_EXISTING); } catch (FileNotFoundException fnfe) { System.err.println("Could not download distribution, defaulting back to local Maven repository"); } piranhaFile = new File(localRepositoryDirectory, artifactPath); } /** * Extract the distribution. */ protected void extractDistribution() { if (piranhaType.equals("zip")) { try (ZipFile zipFile = new ZipFile(piranhaFile)) { File targetDir = new File(runtimeDirectory).getParentFile(); if (!targetDir.exists() && !targetDir.mkdirs()) { System.err.println(UNABLE_TO_CREATE_DIRECTORIES); } zipFile.entries().asIterator().forEachRemaining(zipEntry -> { if (zipEntry.isDirectory()) { File directory = new File(targetDir, zipEntry.getName()); if (!directory.exists() && !directory.mkdirs()) { System.err.println(UNABLE_TO_CREATE_DIRECTORIES); } } else { try { File file = new File(targetDir, zipEntry.getName()); Files.copy(zipFile.getInputStream(zipEntry), file.toPath(), REPLACE_EXISTING); if (zipEntry.getName().toLowerCase().endsWith(".sh") && !file.setExecutable(true)) { System.err.println("Unable to set " + zipEntry.getName() + " to executable"); } } catch (IOException ioe) { ioe.printStackTrace(System.err); } } } ); } catch (IOException ioe) { System.err.println("I/O error occurred opening zip file: " + piranhaFile.toString()); } } } } ================================================ FILE: maven/plugin/src/main/java/cloud/piranha/maven/plugin/RunMojo.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.maven.plugin; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import org.apache.maven.plugin.MojoExecutionException; import static org.apache.maven.plugins.annotations.LifecyclePhase.NONE; import org.apache.maven.plugins.annotations.Mojo; /** * This goal will deploy your web application, start Piranha and wait for it. It * echoes the Piranha console back to you for your convenience. * * @author Manfred Riem (mriem@manorrock.com) */ @Mojo(name = "run", defaultPhase = NONE) public class RunMojo extends BaseMojo { /** * Constructor. */ public RunMojo() { } @Override public void execute() throws MojoExecutionException { if (!skip) { try { determineVersionToUse(); downloadDistribution(); extractDistribution(); copyWarFile(); startPiranhaAndWait(); } catch (IOException ioe) { throw new MojoExecutionException(ioe); } } } /** * Start Piranha using a JAR distribution. */ private void startJarPiranha() throws IOException { ArrayList commands = new ArrayList<>(); commands.add("java"); if (jvmArguments != null && !jvmArguments.equals("")) { commands.addAll(Arrays.asList(jvmArguments.split(" "))); } commands.add("-jar"); commands.add(piranhaFile.getAbsolutePath()); if (contextPath != null) { commands.add("--context-path"); if (contextPath.startsWith("/")) { contextPath = contextPath.substring(1); } commands.add(contextPath); } if (httpPort > 0) { commands.add("--http-port"); commands.add(httpPort.toString()); } if (httpsPort > 0) { commands.add("--https-port"); commands.add(httpsPort.toString()); } if (httpsKeystoreFile != null) { commands.add("--https-keystore-file"); commands.add(httpsKeystoreFile); } if (httpsKeystorePassword != null) { commands.add("--https-keystore-password"); commands.add(httpsKeystorePassword); } commands.add("--war-file"); commands.add(warName + ".war"); commands.add("--write-pid"); Process process = new ProcessBuilder() .directory(new File(runtimeDirectory)) .command(commands) .inheritIO() .start(); System.out.println("Application is available at: http://localhost:" + httpPort + "/" + (contextPath != null ? contextPath : warName)); try { process.waitFor(); } catch (InterruptedException ie) { ie.printStackTrace(System.err); Thread.currentThread().interrupt(); } } /** * Start Piranha using a ZIP distribution. */ private void startZipPiranha() throws IOException { ArrayList commands = new ArrayList<>(); commands.add("/bin/bash"); commands.add("-c"); StringBuilder arguments = new StringBuilder(); arguments.append("./run.sh"); arguments.append(" --http-port ").append(httpPort.toString()); if (contextPath != null) { arguments.append(" --context-path "); if (contextPath.startsWith("/")) { contextPath = contextPath.substring(1); } arguments.append(contextPath); } arguments.append(" --verbose --write-pid"); commands.add(arguments.toString()); Process process = new ProcessBuilder() .directory(new File(runtimeDirectory + File.separator + "bin")) .command(commands) .inheritIO() .start(); System.out.println("Application is available at: http://localhost:" + httpPort + "/" + (contextPath != null ? contextPath : warName)); try { process.waitFor(); } catch (InterruptedException ie) { ie.printStackTrace(System.err); Thread.currentThread().interrupt(); } } /** * Start Piranha. */ private void startPiranhaAndWait() throws IOException { switch (piranhaType) { case "jar" -> startJarPiranha(); case "zip" -> startZipPiranha(); } } } ================================================ FILE: maven/plugin/src/main/java/cloud/piranha/maven/plugin/StartMojo.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.maven.plugin; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import org.apache.maven.plugin.MojoExecutionException; import static org.apache.maven.plugins.annotations.LifecyclePhase.NONE; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; /** * This goal will deploy your web application and start Piranha in a separate * process. * * @author Manfred Riem (mriem@manorrock.com) */ @Mojo(name = "start", defaultPhase = NONE) public class StartMojo extends BaseMojo { /** * Stores the start timeout (in seconds). */ @Parameter(defaultValue = "600", property = "piranha.startTimeout", required = false) protected Integer startTimeout; /** * Default constructor. */ public StartMojo() { } @Override public void execute() throws MojoExecutionException { if (!skip) { try { determineVersionToUse(); downloadDistribution(); extractDistribution(); copyWarFile(); startPiranha(); } catch (IOException ioe) { throw new MojoExecutionException(ioe); } } } /** * Start Piranha using a JAR distribution. */ private void startJarPiranha() throws IOException { ArrayList commands = new ArrayList<>(); commands.add("java"); if (jvmArguments != null && !jvmArguments.equals("")) { commands.addAll(Arrays.asList(jvmArguments.split(" "))); } commands.add("-jar"); commands.add(piranhaFile.getAbsolutePath()); if (contextPath != null) { commands.add("--context-path"); if (contextPath.startsWith("/")) { contextPath = contextPath.substring(1); } commands.add(contextPath); } if (httpPort > 0) { commands.add("--http-port"); commands.add(httpPort.toString()); } if (httpsPort > 0) { commands.add("--https-port"); commands.add(httpsPort.toString()); } if (httpsKeystoreFile != null) { commands.add("--https-keystore-file"); commands.add(httpsKeystoreFile); } if (httpsKeystorePassword != null) { commands.add("--https-keystore-password"); commands.add(httpsKeystorePassword); } commands.add("--war-file"); commands.add(warName + ".war"); commands.add("--write-pid"); new ProcessBuilder() .directory(new File(runtimeDirectory)) .command(commands) .start(); } /** * Start Piranha using a ZIP distribution. */ private void startZipPiranha() throws IOException { ArrayList commands = new ArrayList<>(); StringBuilder arguments = new StringBuilder(); if (System.getProperty("os.name").toLowerCase().contains("windows")) { commands.add("cmd"); commands.add("/c"); arguments.append("start.cmd"); } else { commands.add("/bin/bash"); commands.add("-c"); arguments.append("./start.sh"); } arguments.append(" --http-port ").append(httpPort.toString()); if (contextPath != null) { arguments.append(" --context-path "); if (contextPath.startsWith("/")) { contextPath = contextPath.substring(1); } arguments.append(contextPath); } arguments.append(" --verbose --write-pid"); commands.add(arguments.toString()); new ProcessBuilder() .directory(new File(runtimeDirectory + File.separator + "bin")) .command(commands) .start(); } /** * Start Piranha. */ private void startPiranha() throws IOException { switch (piranhaType) { case "jar" -> startJarPiranha(); case "zip" -> startZipPiranha(); default -> throw new IOException("Unable to determine distribution"); } File pidFile = new File(runtimeDirectory + "/tmp/piranha.pid"); int count = 0; System.out.print("Waiting for Piranha to be ready "); while (!pidFile.exists()) { try { Thread.sleep(1000); count++; System.out.print("."); } catch (InterruptedException ie) { } if (count == startTimeout) { System.out.println(); System.out.println("Warning, PID file not seen!"); break; } } System.out.println(); System.out.println("Application is available at: http://localhost:" + httpPort + "/" + (contextPath != null ? contextPath : warName)); } } ================================================ FILE: maven/plugin/src/main/java/cloud/piranha/maven/plugin/StopMojo.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.maven.plugin; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.concurrent.TimeUnit; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import static org.apache.maven.plugins.annotations.LifecyclePhase.NONE; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; /** * This goal will stop the Piranha runtime that was started with the * start goal. * * @author Manfred Riem (mriem@manorrock.com) */ @Mojo(name = "stop", defaultPhase = NONE) public class StopMojo extends AbstractMojo { /** * Stores the runtime directory. */ @Parameter(defaultValue = "${project.build.directory}/piranha", required = true) private String runtimeDirectory; /** * Stores the skip property. */ @Parameter(defaultValue = "false", property = "piranha.skip") private boolean skip; /** * Constructor. */ public StopMojo() { } @Override public void execute() throws MojoExecutionException { if (!skip) { try { /* * Get the PID from the PID file. */ File pidFile = new File(runtimeDirectory, "tmp/piranha.pid"); String pid = ""; if (pidFile.exists()) { pid = Files.readString(pidFile.toPath()); } /* * Delete the PID file. */ if (!Files.deleteIfExists(pidFile.toPath())) { try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } if (Files.deleteIfExists(new File( runtimeDirectory, "tmp/piranha.pid").toPath())) { System.err.println("Unable to delete PID file"); } } if (!pid.trim().equals("")) { /* * If the process is still active destroy it forcibly. */ ProcessHandle.of(Long.parseLong(pid.trim())).ifPresent(p -> { int count = 0; System.out.print("Waiting for Piranha to stop "); while (p.isAlive()) { try { Thread.sleep(1000); count++; System.out.print("."); } catch (InterruptedException ie) { } if (count == 60) { break; } } System.out.println(); if (p.isAlive()) { System.err.println("Process still alive, destroying forcibly"); p.destroyForcibly(); } }); } } catch (IOException ioe) { throw new MojoExecutionException(ioe); } } } } ================================================ FILE: maven/plugin/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Piranha Maven plugin. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.maven.plugin { exports cloud.piranha.maven.plugin; opens cloud.piranha.maven.plugin; requires maven.plugin.annotations; requires maven.plugin.api; requires java.xml; } ================================================ FILE: maven/plugin/src/site/site.xml ================================================ ================================================ FILE: maven/pom.xml ================================================ 4.0.0 cloud.piranha project 25.4.0-SNAPSHOT cloud.piranha.maven project pom Piranha - Maven archetypes plugin cloud.piranha bom ${project.version} pom import default file:///tmp/piranha/maven/ ================================================ FILE: micro/builder/pom.xml ================================================ 4.0.0 cloud.piranha.micro project 25.4.0-SNAPSHOT piranha-micro-builder jar Piranha - Micro - Builder cloud.piranha piranha-embedded ${project.version} compile cloud.piranha.core piranha-core-impl ${project.version} compile cloud.piranha.micro piranha-micro-loader ${project.version} compile cloud.piranha.resource piranha-resource-impl ${project.version} compile cloud.piranha.resource piranha-resource-shrinkwrap ${project.version} compile true org.jboss.shrinkwrap.descriptors shrinkwrap-descriptors-api-base compile true org.jboss.shrinkwrap.resolver shrinkwrap-resolver-api-maven compile true org.jboss.shrinkwrap.resolver shrinkwrap-resolver-impl-maven compile true org.jboss.shrinkwrap.resolver shrinkwrap-resolver-impl-maven-archive compile true org.jboss.shrinkwrap.resolver shrinkwrap-resolver-spi compile true org.junit.jupiter junit-jupiter-api test org.apache.maven.plugins maven-javadoc-plugin true ================================================ FILE: micro/builder/src/main/java/cloud/piranha/micro/builder/MicroEmbeddedPiranha.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.micro.builder; import cloud.piranha.embedded.EmbeddedPiranha; /** * Embedded variant using Micro * * @author Arjan Tijms */ public class MicroEmbeddedPiranha extends EmbeddedPiranha { /** * Constructor. */ public MicroEmbeddedPiranha() { super(new MicroWebApplication()); } /** * {@return the web application} */ @Override public MicroWebApplication getWebApplication() { return (MicroWebApplication) super.getWebApplication(); } /** * Initialize the web application. * * @return the instance. */ @Override public MicroEmbeddedPiranha initialize() { getWebApplication().initialize(); return this; } /** * Start the web application. * * @return the instance. */ @Override public MicroEmbeddedPiranha start() { getWebApplication().start(); return this; } /** * Stop the web application. * * @return the instance. */ @Override public MicroEmbeddedPiranha stop() { getWebApplication().stop(); return this; } /** * Destroy the web application. * * @return the instance. */ @Override public MicroEmbeddedPiranha destroy() { getWebApplication().destroy(); return this; } } ================================================ FILE: micro/builder/src/main/java/cloud/piranha/micro/builder/MicroEmbeddedPiranhaBuilder.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.micro.builder; import cloud.piranha.core.api.PiranhaBuilder; import org.jboss.shrinkwrap.api.Archive; import cloud.piranha.micro.loader.MicroConfiguration; import cloud.piranha.micro.loader.MicroOuterDeployer; /** * Builder for an embedded Piranha instance based on Piranha Micro * * @author Arjan Tijms */ public class MicroEmbeddedPiranhaBuilder implements PiranhaBuilder { /** * Object containing all configuration settings for Piranha Micro */ private MicroConfiguration configuration; /** * Application archive that will be executed by Piranha Micro */ private Archive archive; /** * Constructor */ public MicroEmbeddedPiranhaBuilder() { } /** * Sets the configuration for Piranha Micro * * @param configuration the configuration * @return instance of this builder */ public MicroEmbeddedPiranhaBuilder configuration(MicroConfiguration configuration) { this.configuration = configuration; return this; } /** * Set the application archive that will be loaded and executed by Piranha * Micro * * @param archive the archive to be executed * @return instance of this builder */ public MicroEmbeddedPiranhaBuilder archive(Archive archive) { this.archive = archive; return this; } @Override public MicroEmbeddedPiranha build() { MicroEmbeddedPiranha microEmbeddedPiranha = new MicroEmbeddedPiranha(); MicroWebApplication microWebApplication = microEmbeddedPiranha.getWebApplication(); if (configuration == null) { // Use default config is nothing set configuration = new MicroConfiguration(); configuration.setHttpStart(false); } if (configuration.getContextPath() != null) { // If an explicit root is set, use it. Otherwise use the default. microWebApplication.setContextPath(configuration.getContextPath()); } microWebApplication.setDeployedApplication( new MicroOuterDeployer(configuration.postConstruct()).deploy(archive).getDeployedApplication()); return microEmbeddedPiranha; } /** * Builds an embedded Piranha Micro instance and deploys the archive set by * this builder to it. * * @return the newly created Piranha Micro instance */ public MicroEmbeddedPiranha buildAndStart() { MicroEmbeddedPiranha microEmbeddedPiranha = build(); if (!configuration.isHttpStart()) { ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); try { MicroWebApplication microWebApplication = microEmbeddedPiranha.getWebApplication(); Thread.currentThread().setContextClassLoader(microWebApplication.getClassLoader()); microWebApplication.initialize(); microWebApplication.start(); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } return microEmbeddedPiranha; } } ================================================ FILE: micro/builder/src/main/java/cloud/piranha/micro/builder/MicroWebApplication.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.micro.builder; import static java.util.Map.entry; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Consumer; import cloud.piranha.core.api.WebApplicationRequest; import cloud.piranha.core.api.WebApplicationResponse; import cloud.piranha.core.impl.DefaultWebApplication; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; /** * A Piranha Micro web application. * * @author Arjan Tijms */ public class MicroWebApplication extends DefaultWebApplication { /** * Runnable to do nothing */ private final Runnable doNothing = new Runnable() { @Override public void run() { // do nothing ;) } }; /** * Stores the deployed application. */ private Consumer> deployedApplication; /** * Constructor. */ public MicroWebApplication() { super(); } /** * {@return the deployed application} */ public Consumer> getDeployedApplication() { return deployedApplication; } /** * Set the deployed application. * * @param deployedApplication the deployed application. */ public void setDeployedApplication(Consumer> deployedApplication) { this.deployedApplication = deployedApplication; } /** * Service the request. * * @param request the request. * @param response the resposne. */ @Override public void service(ServletRequest request, ServletResponse response) { deployedApplication.accept(copyApplicationRequestToMap((WebApplicationRequest) request, (WebApplicationResponse) response)); } /** * Copy the request and response to a map. * * @param applicationRequest the web application request. * @param applicationResponse the web application response. * @return the map. */ private Map copyApplicationRequestToMap(WebApplicationRequest applicationRequest, WebApplicationResponse applicationResponse) { Map requestValues = new HashMap<>(); requestValues.putAll(requestToMap(applicationRequest)); requestValues.putAll(responseToMap(applicationResponse)); return requestValues; } /** * Get a map of request. * * @param request the web application request. * @return the map. */ private Map requestToMap(WebApplicationRequest request) { return Map.ofEntries( entry("LocalAddr", request.getLocalAddr()), entry("LocalName", request.getLocalName()), entry("LocalPort", request.getLocalPort()), entry("RemoteAddr", request.getRemoteAddr()), entry("RemoteHost", request.getRemoteHost()), entry("RemotePort", request.getRemotePort()), entry("ServerName", request.getServerName()), entry("ServerPort", request.getServerPort()), entry("Method", request.getMethod()), entry("ContextPath", request.getContextPath()), entry("ServletPath", request.getServletPath()), entry("QueryString", request.getQueryString() == null ? "" : request.getQueryString()), entry("InputStream", getInputStreamUnchecked(request)), entry("Headers", getHeadersAsMap(request))); } /** * Get the input stream unchecked. * * @param request the web application request. * @retunr the unchecked input stream. */ private InputStream getInputStreamUnchecked(WebApplicationRequest request) { try { return request.getInputStream(); } catch (IOException e) { throw new UncheckedIOException(e); } } /** * Get the headers as a map. * * @param request the web application request. * @return the map. */ private Map> getHeadersAsMap(WebApplicationRequest request) { Map> headers = new HashMap<>(); Enumeration headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String name = headerNames.nextElement(); String value = request.getHeader(name); headers.computeIfAbsent(name, e -> new ArrayList<>()).add(value); } return headers; } /** * Get a map of underlying output stream and response closer. * * @param response the web application response. * @return the map. */ private Map responseToMap(WebApplicationResponse response) { return Map.of( "WebApplicationOutputStream", response.getWebApplicationOutputStream(), "ResponseCloser", response.getResponseCloser() == null ? doNothing : response.getResponseCloser()); } } ================================================ FILE: micro/builder/src/main/java/cloud/piranha/micro/builder/package-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** *

* This package delivers you with an embeddable Servlet container that hosts * only a single application without exposing an HTTP endpoint. It is used * extensively within the Piranha project itself to test all the Servlet * functionality. *

* *

* The image below illustrates how the request and response handling is done by * Piranha Embedded. When an {@link cloud.piranha.embedded.EmbeddedRequest} * comes in it uses a * {@link cloud.piranha.core.api.WebApplicationRequestMapper} to determine * which FilterChain needs to process the incoming request. *

* *

* Embedded request and response handling *

* *

How do I use Piranha Embedded?

* *

* See our documentation for more * information. *

* * @author Manfred Riem (mriem@manorrock.com) * @see cloud.piranha.embedded.EmbeddedPiranhaBuilder */ package cloud.piranha.micro.builder; ================================================ FILE: micro/builder/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the builder for Piranha Micro. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.micro.builder { exports cloud.piranha.micro.builder; opens cloud.piranha.micro.builder; requires transitive cloud.piranha.core.api; requires cloud.piranha.core.impl; requires cloud.piranha.embedded; requires transitive cloud.piranha.micro.loader; requires cloud.piranha.resource.shrinkwrap; requires shrinkwrap.api; } ================================================ FILE: micro/core/pom.xml ================================================ 4.0.0 cloud.piranha.micro project 25.4.0-SNAPSHOT piranha-micro-core Piranha - Micro - Core This project delivers the Piranha Micro Core code. It sets up the "global" environment for a micro instance, such as JNDI, a URL handler, a default identity store, and the annotation index. It creates a web application that it either returns or deploys itself to an embedded HTTP server. cloud.piranha bom ${project.version} pom import cloud.piranha.core piranha-core-impl ${project.version} compile cloud.piranha.http piranha-http-impl ${project.version} compile cloud.piranha.http piranha-http-webapp ${project.version} compile cloud.piranha.extension piranha-extension-annotationscan ${project.version} compile io.smallrye jandex compile cloud.piranha.resource piranha-resource-shrinkwrap ${project.version} compile org.jboss.shrinkwrap.descriptors shrinkwrap-descriptors-api-base compile org.jboss.shrinkwrap.resolver shrinkwrap-resolver-api-maven compile org.jboss.shrinkwrap.resolver shrinkwrap-resolver-impl-maven compile org.jboss.shrinkwrap.resolver shrinkwrap-resolver-impl-maven-archive compile org.jboss.shrinkwrap.resolver shrinkwrap-resolver-spi compile cloud.piranha.extension piranha-extension-security-jakarta ${project.version} provided cloud.piranha.extension piranha-extension-weld ${project.version} provided jakarta.ws.rs jakarta.ws.rs-api provided org.junit.jupiter junit-jupiter-api test org.apache.maven.plugins maven-jar-plugin true ================================================ FILE: micro/core/src/main/java/cloud/piranha/micro/core/CdiExtension.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.micro.core; import jakarta.enterprise.event.Observes; import jakarta.enterprise.inject.spi.BeanManager; import jakarta.enterprise.inject.spi.BeforeBeanDiscovery; import jakarta.enterprise.inject.spi.Extension; /** * This extension registers an identity store in case callers (users) / * credentials have been added to it. * * @author Arjan Tijms */ public class CdiExtension implements Extension { /** * Constructor. */ public CdiExtension() { } /** * Register the in-memory identity store. * * @param beforeBeanDiscovery the before bean discovery. * @param beanManager the bean manager. */ public void register(@Observes BeforeBeanDiscovery beforeBeanDiscovery, BeanManager beanManager) { if (!InMemoryIdentityStore.getCallerToCredentials().isEmpty()) { beforeBeanDiscovery.addAnnotatedType( beanManager.createAnnotatedType(InMemoryIdentityStore.class), "Piranha " + InMemoryIdentityStore.class.getName()); } } } ================================================ FILE: micro/core/src/main/java/cloud/piranha/micro/core/DefaultAnnotationInfo.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.micro.core; import cloud.piranha.core.api.AnnotationInfo; import java.lang.reflect.AnnotatedElement; /** * The default AnnotationInfo. * * @author Arjan Tijms * @author Manfred Riem (mriem@manorrock.com) * @param the type. */ public class DefaultAnnotationInfo implements AnnotationInfo { /** * Stores the instance. */ private final T instance; /** * Stores the target. */ private final AnnotatedElement target; /** * Constructor. * * @param instance the instance. * @param target the target annotated element. */ public DefaultAnnotationInfo(T instance, AnnotatedElement target) { this.instance = instance; this.target = target; } @Override public T getInstance() { return instance; } @Override public AnnotatedElement getTarget() { return target; } } ================================================ FILE: micro/core/src/main/java/cloud/piranha/micro/core/InMemoryIdentityStore.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.micro.core; import static java.util.Collections.emptySet; import static jakarta.security.enterprise.identitystore.CredentialValidationResult.INVALID_RESULT; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import jakarta.enterprise.context.ApplicationScoped; import jakarta.security.enterprise.CallerPrincipal; import jakarta.security.enterprise.credential.UsernamePasswordCredential; import jakarta.security.enterprise.identitystore.CredentialValidationResult; import jakarta.security.enterprise.identitystore.IdentityStore; /** * A basic in-memory identity store. * *

* This identity store functions as the default identity store for among others * Servlet security. * * @author Arjan Tijms */ @ApplicationScoped public class InMemoryIdentityStore implements IdentityStore { /** * The credential. * * @author Manfred Riem (mriem@manorrock.com) */ public static class Credential { /** * Stores the caller name. */ private final String callerName; /** * Stores the password. */ private final String password; /** * Stores the groups. */ private final List groups; /** * Constructor. * * @param callerName the caller name. * @param password the password. * @param groups the groups. */ public Credential(String callerName, String password, List groups) { super(); this.callerName = callerName; this.password = password; this.groups = groups; } /** * {@return the caller name} */ public String getCallerName() { return callerName; } /** * {@return the password} */ public String getPassword() { return password; } /** * {@return the groups} */ public List getGroups() { return groups; } } /** * Stores the caller to credentials map. */ private static final Map CALLER_TO_CREDENTIALS = new ConcurrentHashMap<>(); /** * Constructor. */ public InMemoryIdentityStore() { super(); } /** * {@return the caller to credentials map} */ public static Map getCallerToCredentials() { return CALLER_TO_CREDENTIALS; } /** * Add the credential. * * @param callerName the caller name. * @param password the password. * @param groups the groups. */ public static void addCredential(String callerName, String password, List groups) { addCredential(new Credential(callerName, password, groups)); } /** * Add the credential. * * @param credential the credential. */ public static void addCredential(Credential credential) { CALLER_TO_CREDENTIALS.put(credential.getCallerName(), credential); } /** * Validate the username password credential. * * @param usernamePasswordCredential the username password credential. * @return the credential validation result. */ public CredentialValidationResult validate(UsernamePasswordCredential usernamePasswordCredential) { Credential credential = CALLER_TO_CREDENTIALS.get(usernamePasswordCredential.getCaller()); if (credential != null && usernamePasswordCredential.getPassword().compareTo(credential.getPassword())) { return new CredentialValidationResult( new CallerPrincipal(credential.getCallerName()), new HashSet<>(credential.getGroups()) ); } return INVALID_RESULT; } @Override public Set getCallerGroups(CredentialValidationResult validationResult) { Credential credentials = CALLER_TO_CREDENTIALS.get(validationResult.getCallerPrincipal().getName()); return credentials != null ? new HashSet<>(credentials.getGroups()) : emptySet(); } } ================================================ FILE: micro/core/src/main/java/cloud/piranha/micro/core/MicroInnerApplication.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.micro.core; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationOutputStream; import cloud.piranha.core.api.WebApplicationInputStream; import cloud.piranha.core.impl.CookieParser; import cloud.piranha.core.impl.DefaultWebApplicationRequest; import cloud.piranha.core.impl.DefaultWebApplicationResponse; import jakarta.servlet.ServletException; import jakarta.servlet.http.Cookie; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Stream; /** * The inner Piranha Micro application. * * @author Manfred Riem (mriem@manorrock.com) */ public class MicroInnerApplication implements Consumer> { /** * Stores the logger. */ private static final Logger LOGGER = Logger.getLogger(MicroInnerApplication.class.getName()); /** * Stores the web application. */ private final WebApplication webApplication; /** * Constructor. * * @param webApplication the web application. */ public MicroInnerApplication(WebApplication webApplication) { this.webApplication = webApplication; } @Override public void accept(Map requestMap) { ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(webApplication.getClassLoader()); webApplication.service(copyMapToApplicationRequest(requestMap), copyMapToApplicationResponse(requestMap)); } catch (ServletException | IOException e) { LOGGER.log(Level.WARNING, "An error occurred servicing request", e); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } @SuppressWarnings("unchecked") private DefaultWebApplicationRequest copyMapToApplicationRequest(Map requestMap) { DefaultWebApplicationRequest applicationRequest = new DefaultWebApplicationRequest(); applicationRequest.setLocalAddr((String) requestMap.get("Address")); applicationRequest.setLocalName((String) requestMap.get("LocalName")); applicationRequest.setLocalPort((Integer) requestMap.get("LocalPort")); applicationRequest.setRemoteAddr((String) requestMap.get("RemoteAddr")); applicationRequest.setRemoteHost((String) requestMap.get("RemoteHost")); applicationRequest.setRemotePort((int) requestMap.get("RemotePort")); applicationRequest.setServerName((String) requestMap.get("ServerName")); applicationRequest.setServerPort((Integer) requestMap.get("ServerPort")); applicationRequest.setMethod((String) requestMap.get("Method")); applicationRequest.setContextPath((String) requestMap.get("ContextPath")); applicationRequest.setServletPath((String) requestMap.get("ServletPath")); applicationRequest.setQueryString((String) requestMap.get("QueryString")); applicationRequest.setWebApplicationInputStream((WebApplicationInputStream) requestMap.get("WebApplicationInputStream")); for (Map.Entry> headerEntry : ((Map>) requestMap.get("Headers")).entrySet()) { String name = headerEntry.getKey(); List values = headerEntry.getValue(); for (String value : values) { applicationRequest.setHeader(name, value); if (name.equalsIgnoreCase("Content-Type")) { applicationRequest.setContentType(value); } if (name.equalsIgnoreCase("Content-Length")) { applicationRequest.setContentLength(Integer.parseInt(value)); } if (name.equalsIgnoreCase("COOKIE")) { applicationRequest.setCookies(processCookies(applicationRequest, value)); } } } applicationRequest.setWebApplication(webApplication); return applicationRequest; } private static Cookie[] processCookies(DefaultWebApplicationRequest result, String cookiesValue) { Cookie[] cookies = CookieParser.parse(cookiesValue); Stream.of(cookies) .filter(x -> "JSESSIONID".equals(x.getName())) .findAny() .ifPresent(cookie -> { result.setRequestedSessionIdFromCookie(true); result.setRequestedSessionId(cookie.getValue()); }); return cookies; } private DefaultWebApplicationResponse copyMapToApplicationResponse(Map requestMap) { DefaultWebApplicationResponse response = new DefaultWebApplicationResponse(); response.setWebApplicationOutputStream((WebApplicationOutputStream) requestMap.get("WebApplicationOutputStream")); response.setResponseCloser((Runnable) requestMap.get("ResponseCloser")); return response; } } ================================================ FILE: micro/core/src/main/java/cloud/piranha/micro/core/MicroInnerDeployer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.micro.core; import static java.lang.Boolean.TRUE; import static java.lang.System.Logger.Level.INFO; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Arrays.asList; import static java.util.Arrays.stream; import static javax.xml.xpath.XPathConstants.NODESET; import static org.jboss.jandex.AnnotationTarget.Kind.CLASS; import static org.jboss.jandex.AnnotationTarget.Kind.FIELD; import static org.jboss.jandex.AnnotationTarget.Kind.METHOD; import static org.jboss.jandex.DotName.createSimple; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.System.Logger; import java.lang.System.Logger.Level; import java.lang.annotation.Annotation; import java.net.URL; import java.net.URLConnection; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.ServiceLoader; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Stream; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationTarget; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.Index; import org.jboss.jandex.IndexReader; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.api.Node; import org.jboss.shrinkwrap.api.asset.ArchiveAsset; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import cloud.piranha.core.api.AnnotationManager; import cloud.piranha.core.api.WebApplication; import cloud.piranha.core.api.WebApplicationExtension; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.core.impl.DefaultWebApplicationExtensionContext; import cloud.piranha.extension.annotationscan.internal.InternalAnnotationScanAnnotationManager; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.impl.DefaultHttpServer; import cloud.piranha.http.webapp.HttpWebApplicationServer; import cloud.piranha.resource.shrinkwrap.GlobalArchiveStreamHandler; import cloud.piranha.resource.shrinkwrap.ShrinkWrapResource; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import jakarta.annotation.Priority; import jakarta.annotation.Resource; import jakarta.annotation.Resources; import jakarta.annotation.security.DeclareRoles; import jakarta.annotation.security.DenyAll; import jakarta.annotation.security.PermitAll; import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RunAs; import jakarta.servlet.annotation.HandlesTypes; import jakarta.servlet.annotation.MultipartConfig; import jakarta.servlet.annotation.ServletSecurity; import jakarta.servlet.annotation.WebFilter; import jakarta.servlet.annotation.WebInitParam; import jakarta.servlet.annotation.WebListener; import jakarta.servlet.annotation.WebServlet; /** * Deploys a shrinkwrap application archive to a newly started embedded Piranha * instance. * *

* This class is expected to be run within in its own inner (isolated) class * loader * * @author arjan */ public class MicroInnerDeployer { /** * Defines the attribute name for the MicroPiranha reference. */ static final String MICRO_PIRANHA = "cloud.piranha.micro.MicroPiranha"; /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(MicroInnerDeployer.class.getName()); /** * Stores the web annotations. */ String[] webAnnotations = new String[]{ // Servlet WebServlet.class.getName(), WebListener.class.getName(), WebInitParam.class.getName(), WebFilter.class.getName(), ServletSecurity.class.getName(), MultipartConfig.class.getName(), // REST "jakarta.ws.rs.Path", "jakarta.ws.rs.ext.Provider", "jakarta.ws.rs.ApplicationPath", // Faces "jakarta.faces.lifecycle.ClientWindowScoped", "jakarta.faces.component.behavior.FacesBehavior", "jakarta.faces.render.FacesBehaviorRenderer", "jakarta.faces.component.FacesComponent", "jakarta.faces.annotation.FacesConfig", "jakarta.faces.convert.FacesConverter", "jakarta.faces.model.FacesDataModel", "jakarta.faces.render.FacesRenderer", "jakarta.faces.validator.FacesValidator", "jakarta.faces.flow.builder.FlowBuilderParameter", "jakarta.faces.flow.builder.FlowDefinition", "jakarta.faces.flow.FlowScoped", "jakarta.faces.event.ListenerFor", "jakarta.faces.event.ListenersFor", "jakarta.faces.event.NamedEvent", "jakarta.faces.push.Push", "jakarta.faces.application.ResourceDependencies", "jakarta.faces.application.ResourceDependency", "jakarta.faces.view.ViewScoped", // Persistence "jakarta.persistence.Entity", "jakarta.persistence.Embeddable", "jakarta.persistence.Converter", "jakarta.persistence.MappedSuperclass", // General DeclareRoles.class.getName(), // Not Servlet, but often used on Servlets DenyAll.class.getName(), PermitAll.class.getName(), RolesAllowed.class.getName(), RunAs.class.getName(), PostConstruct.class.getName(), PreDestroy.class.getName(), Priority.class.getName(), Resource.class.getName(), Resources.class.getName(),}; /** * Stores the instances. */ String[] instances = new String[]{ // REST "jakarta.ws.rs.core.Application", // Faces "jakarta.faces.convert.Converter", "jakarta.faces.model.DataModel", "jakarta.faces.event.PhaseListener", "jakarta.faces.render.Renderer", "jakarta.faces.component.UIComponent", "jakarta.faces.validator.Validator", }; /** * Stores the HTTP server. */ private HttpServer httpServer; /** * Constructor. */ public MicroInnerDeployer() { } /** * Start the application. * * @param applicationArchive the application archive. * @param classLoader the classloader. * @param handlers the handlers. * @param config the configuration. * @return the map. */ public Map start(Archive applicationArchive, ClassLoader classLoader, Map> handlers, Map config) { try { WebApplication webApplication = getWebApplication(applicationArchive, classLoader); LOGGER.log(INFO, "Starting web application " + applicationArchive.getName() + " on Piranha Micro " + webApplication.getAttribute(MICRO_PIRANHA)); // The global archive stream handler is set to resolve "shrinkwrap://" URLs (created from strings). // Such URLs come into being primarily when code takes resolves a class or resource from the class loader by URL // and then takes the string form of the URL representing the class or resource. GlobalArchiveStreamHandler streamHandler = new GlobalArchiveStreamHandler(webApplication); // Life map to the StaticURLStreamHandlerFactory used by the root class loader handlers.put("shrinkwrap", streamHandler::connect); // Source of annotations Index index = getIndex(); // Target of annotations AnnotationManager annotationManager = new InternalAnnotationScanAnnotationManager(); webApplication.getManager().setAnnotationManager(annotationManager); // Copy annotations from our "annotations" collection from source index to target manager forEachWebAnnotation(webAnnotation -> addAnnotationToIndex(index, webAnnotation, annotationManager)); // Collect sub-classes/interfaces of our "instances" collection from source index to target manager forEachInstance(instanceClass -> addInstanceToIndex(index, instanceClass, annotationManager)); // Collect any sub-classes/interfaces from any HandlesTypes annotation getAnnotations(index, HandlesTypes.class) .map(this::getTarget) .forEach(annotationTarget -> getAnnotationInstances(annotationTarget, HandlesTypes.class) .map(HandlesTypes.class::cast) .forEach(handlesTypesInstance -> stream(handlesTypesInstance.value()).forEach(e -> { if (e.isAnnotation()) { addAnnotationToIndex(index, e, annotationManager); } else { addInstanceToIndex(index, e, annotationManager); } }))); // Setup the default identity store, which is used as the default "username and roles database" for // (Servlet) security. initIdentityStore(webApplication); setApplicationContextPath(webApplication, config, applicationArchive); DefaultWebApplicationExtensionContext extensionContext = new DefaultWebApplicationExtensionContext(); for (WebApplicationExtension extension : ServiceLoader.load(WebApplicationExtension.class)) { extensionContext.add(extension); } extensionContext.configure(webApplication); webApplication.initialize(); webApplication.start(); if ((boolean) config.get("micro.http.start")) { HttpWebApplicationServer webApplicationServer = new HttpWebApplicationServer(); webApplicationServer.addWebApplication(webApplication); httpServer = new DefaultHttpServer(); httpServer.setServerPort((Integer) config.get("micro.port")); httpServer.setSSL(Boolean.getBoolean("piranha.http.ssl")); httpServer.setHttpServerProcessor(webApplicationServer); httpServer.start(); } return Map.of( "deployedServlets", webApplication.getServletRegistrations().keySet(), "deployedApplication", new MicroInnerApplication(webApplication), "deployedContextRoot", webApplication.getContextPath()); } catch (IOException e) { throw new IllegalStateException(e); } catch (Exception e) { throw e; } } void setApplicationContextPath(WebApplication webApplication, Map config, Archive applicationArchive) { if (TRUE.equals(config.get("micro.rootIsWarName"))) { String contextPath = applicationArchive.getName(); if (contextPath != null && contextPath.endsWith(".war")) { contextPath = "/" + contextPath.substring(0, contextPath.length()-4); } webApplication.setContextPath(contextPath); } else { String contextPath = (String) config.get("micro.root"); if (contextPath != null) { webApplication.setContextPath(contextPath); } } } WebApplication getWebApplication(Archive archive, ClassLoader newClassLoader) { WebApplication webApplication = new DefaultWebApplication(); webApplication.setClassLoader(newClassLoader); // The main resource representing the (war) archive itself. webApplication.addResource(new ShrinkWrapResource(archive)); // Get the list of embedded archives containing a "/META-INF/resources" folder. Node resourceNodes = archive.get("/META-INF/piranha/resource-libs"); if (resourceNodes != null) { for (Node resourceNode : resourceNodes.getChildren()) { ArchiveAsset resourceArchiveAsset = (ArchiveAsset) resourceNode.getAsset(); // Add the archive as a resource with the "/META-INF/resources" folder shifted to its root webApplication.addResource(new ShrinkWrapResource("/META-INF/resources", resourceArchiveAsset.getArchive())); } } return webApplication; } /** * Stop the application. */ public void stop() { if (httpServer != null) { httpServer.stop(); } } Index getIndex() { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); try (InputStream indexStream = classLoader.getResourceAsStream("META-INF/piranha.idx")) { return new IndexReader(indexStream).read(); } catch (IOException e) { throw new IllegalStateException(e); } } void forEachWebAnnotation(Consumer> consumer) { stream(webAnnotations) .map(this::toClass) .flatMap(Optional::stream) .map(e -> (Class) e) .forEach(consumer); } void addAnnotationToIndex(Index index, Class webAnnotation, AnnotationManager annotationManager) { getAnnotations(index, webAnnotation) // Get the annotation target and annotation instance corresponding to the // (raw/abstract) indexed annotation .map(this::getTarget) .filter(Objects::nonNull) .forEach(annotationTarget -> getAnnotationInstances(annotationTarget, webAnnotation) .forEach(annotationInstance -> // Store the matching annotation instance (@WebServlet(name=...) // and annotation target (@WebServlet public class Target) in the manager annotationManager.addAnnotation( new DefaultAnnotationInfo<>(annotationInstance, annotationTarget)))); } void addInstanceToIndex(Index index, Class instanceClass, AnnotationManager annotationManager) { getInstances(index, instanceClass) .map(this::getTarget) .forEach(implementingClass -> annotationManager.addInstance(instanceClass, implementingClass)); } Optional> toClass(String className) { try { return Optional.of(Class.forName(className, true, Thread.currentThread().getContextClassLoader())); } catch (ClassNotFoundException e) { return Optional.empty(); } } Stream getAnnotations(Index index, Class webAnnotation) { return index.getAnnotations( createSimple(webAnnotation.getName())) .stream(); } Stream getInstances(Index index, Class instanceClass) { return Stream.concat( index.getAllKnownSubclasses( createSimple(instanceClass.getName())) .stream(), index.getAllKnownImplementors( createSimple(instanceClass.getName())) .stream()); } Class getTarget(AnnotationInstance annotationInstance) { return getTarget(annotationInstance.target()); } Class getTarget(AnnotationTarget target) { try { if (target.kind() == CLASS) { return Class.forName( target.asClass().toString(), false, Thread.currentThread().getContextClassLoader()); } if (target.kind() == FIELD) { return Class.forName( target.asField().declaringClass().toString(), false, Thread.currentThread().getContextClassLoader()); } if (target.kind() == METHOD) { return Class.forName( target.asMethod().declaringClass().toString(), false, Thread.currentThread().getContextClassLoader()); } return null; } catch (ClassNotFoundException e) { throw new IllegalStateException(e); } } Stream getAnnotationInstances(Class target, Class annotationType) { return stream(target.getAnnotations()) .filter(e -> e.annotationType().isAssignableFrom(annotationType)); } void forEachInstance(Consumer> consumer) { stream(instances) .map(this::toClass) .flatMap(Optional::stream) .map(e -> (Class) e) .forEach(consumer); } void getCallerCredentials(String callersAsXml) { if (isEmpty(callersAsXml)) { return; } try { XPath xPath = XPathFactory .newInstance() .newXPath(); NodeList nodes = (NodeList) xPath .evaluate( "//caller", DocumentBuilderFactory .newInstance() .newDocumentBuilder() .parse(new ByteArrayInputStream(callersAsXml.getBytes())), NODESET); for (int i = 0; i < nodes.getLength(); i++) { NamedNodeMap callerAttributes = nodes.item(i).getAttributes(); String caller = callerAttributes.getNamedItem("callername").getNodeValue(); String password = callerAttributes.getNamedItem("password").getNodeValue(); String groups = callerAttributes.getNamedItem("groups").getNodeValue(); InMemoryIdentityStore.addCredential(caller, password, asList(groups.split(","))); } } catch (SAXException | IOException | ParserConfigurationException | XPathExpressionException e) { LOGGER.log(Level.WARNING, "Unable to get caller credentials", e); } } void initIdentityStore(WebApplication webApplication) throws IOException { String callers = System.getProperty("io.piranha.identitystore.callers"); if (callers == null) { InputStream xmlStream = webApplication.getResourceAsStream("WEB-INF/piranha-callers.xml"); if (xmlStream != null) { callers = new String(xmlStream.readAllBytes(), UTF_8); } } getCallerCredentials(callers); } private boolean isEmpty(String string) { return string == null || string.isEmpty(); } } ================================================ FILE: micro/core/src/main/java/cloud/piranha/micro/core/PiranhaBeanArchiveHandler.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.micro.core; import static org.jboss.weld.environment.deployment.discovery.jandex.Jandex.INDEX_ATTRIBUTE_NAME; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import jakarta.annotation.Priority; import org.jboss.jandex.Index; import org.jboss.jandex.IndexReader; import org.jboss.weld.environment.deployment.discovery.BeanArchiveBuilder; import org.jboss.weld.environment.deployment.discovery.BeanArchiveHandler; /** * An Archive handler for Weld that implicitly reads the classes in * an archive deployed to Piranha via the META-INF/piranha.idx file. * * @author Arjan Tijms */ @Priority(10) public class PiranhaBeanArchiveHandler implements BeanArchiveHandler { /** * Constructor. */ public PiranhaBeanArchiveHandler() { } @Override public BeanArchiveBuilder handle(String beanArchiveReference) { String indexURL = null; // We're only handling the classes in the application archive, which is represented // by /WEB-INF/classes if ("/WEB-INF/classes".equals(beanArchiveReference)) { indexURL = "shrinkwrap://cloud.piranha.modular.classes/META-INF/jandex.idx"; } else { try { @SuppressWarnings("deprecation") URL url = new URL(beanArchiveReference); String protocol = url.getProtocol(); if (!protocol.equals("shrinkwrap")) { return null; } indexURL = "shrinkwrap://" + url.getHost() + "/META-INF/jandex.idx"; } catch (MalformedURLException e1) { throw new IllegalStateException(e1); } } // The beanArchiveBuilder is a builder the native archive type for Weld. // It roughly corresponds to a Shrinkwrap Archive builder. BeanArchiveBuilder beanArchiveBuilder = new BeanArchiveBuilder(); // Get the class and annotation index Index index = getIndex(indexURL); beanArchiveBuilder.setAttribute(INDEX_ATTRIBUTE_NAME, index); // Populate the Weld Archive with all the classes from the index, representing // the original application archive. index.getKnownClasses() .stream() .map(e -> e.asClass().name().toString()) .forEach(beanArchiveBuilder::addClass); return beanArchiveBuilder; } Index getIndex(String indexURL) { try (InputStream indexStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(indexURL)) { return new IndexReader(indexStream).read(); } catch (IOException e) { throw new IllegalStateException(e); } } } ================================================ FILE: micro/core/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ import cloud.piranha.micro.core.CdiExtension; import cloud.piranha.micro.core.PiranhaBeanArchiveHandler; import cloud.piranha.core.api.WebApplicationExtension; import jakarta.enterprise.inject.spi.Extension; import org.jboss.weld.environment.deployment.discovery.BeanArchiveHandler; /** * This module delivers the core of Piranha Micro. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.micro.core { exports cloud.piranha.micro.core; opens cloud.piranha.micro.core; provides BeanArchiveHandler with PiranhaBeanArchiveHandler; provides Extension with CdiExtension; requires transitive cloud.piranha.core.api; requires cloud.piranha.core.impl; requires cloud.piranha.extension.annotationscan; requires cloud.piranha.extension.security.jakarta; requires cloud.piranha.extension.weld; requires cloud.piranha.http.api; requires cloud.piranha.http.impl; requires cloud.piranha.http.webapp; requires cloud.piranha.resource.shrinkwrap; requires jakarta.annotation; requires transitive jakarta.cdi; requires transitive jakarta.security; requires java.logging; requires java.naming; requires java.xml; requires org.jboss.jandex; requires shrinkwrap.api; requires transitive weld.environment.common; uses WebApplicationExtension; } ================================================ FILE: micro/core/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension ================================================ cloud.piranha.micro.core.CdiExtension ================================================ FILE: micro/core/src/main/resources/META-INF/services/org.jboss.weld.environment.deployment.discovery.BeanArchiveHandler ================================================ cloud.piranha.micro.core.PiranhaBeanArchiveHandler ================================================ FILE: micro/loader/pom.xml ================================================ 4.0.0 cloud.piranha.micro project 25.4.0-SNAPSHOT piranha-micro-loader jar Piranha - Micro - Loader cloud.piranha.core piranha-core-impl ${project.version} true cloud.piranha.resource piranha-resource-impl ${project.version} true cloud.piranha.resource piranha-resource-shrinkwrap ${project.version} true org.apache.commons commons-compress org.jboss.shrinkwrap.descriptors shrinkwrap-descriptors-api-base true org.jboss.shrinkwrap.resolver shrinkwrap-resolver-api-maven true org.jboss.shrinkwrap.resolver shrinkwrap-resolver-spi true org.jboss.shrinkwrap.resolver shrinkwrap-resolver-impl-maven true org.jboss.shrinkwrap.resolver shrinkwrap-resolver-impl-maven-archive true com.google.guava guava io.smallrye jandex true org.junit.jupiter junit-jupiter-api test org.apache.maven.plugins maven-compiler-plugin default-compile module-info.java patch-compile compile compile --patch-module=shrinkwrap.resolver.api.maven=${settings.localRepository}/org/jboss/shrinkwrap/resolver/shrinkwrap-resolver-api/3.3.3/shrinkwrap-resolver-api-3.3.3.jar org.moditect moditect-maven-plugin add-module-infos package add-module-info true ${project.build.sourceDirectory}/module-info.java ================================================ FILE: micro/loader/src/main/java/cloud/piranha/micro/loader/MicroConfiguration.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.micro.loader; import static java.util.Arrays.stream; import static java.util.function.Function.identity; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Stream; /** * The configuration for Piranha Micro. * * @author Arjan Tijms */ public class MicroConfiguration { /** * Stores the HTTP server implementation. */ private String httpServer; /** * Stores the HTTP start flag. */ private boolean httpStart; /** * Stores the version. */ private String version; /** * Stores the extensions. */ private String extensions; /** * Stores the dependencies. */ private String dependencies; /** * Stores the repositories. */ private String repositories; /** * Stores the offline flag. */ private boolean offline; /** * Stores the port. */ private int port; /** * If true, the context root for the web app is taken from the war name. */ private boolean rootIsWarName; /** * Stores the root. */ private String root; /** * Stores the list of extensions. */ private List extensionsList; /** * Stores the list of repositories. */ private List repositoriesList; /** * Stores the merged dependencies. */ private List mergedDependencies; /** * Default constructor. Initializes most of the stuff from System * properties. */ public MicroConfiguration() { this( System.getProperty("piranha.version", MicroConfiguration.class.getPackage().getImplementationVersion()), System.getProperty("piranha.extensions", "micro"), System.getProperty("piranha.dependencies", ""), System.getProperty("piranha.repositories", "https://repo1.maven.org/maven2"), Boolean.valueOf(System.getProperty("piranha.offline", "false")), Integer.valueOf(System.getProperty("piranha.port", "8080")), Boolean.valueOf(System.getProperty("piranha.rootIsWarName", "false")), System.getProperty("piranha.root"), System.getProperty("piranha.http.server", "impl"), Boolean.valueOf(System.getProperty("piranha.http.start", "true")), null, null, null); } /** * Constructor. * * @param version Piranha version. * @param extensions Piranha extensions. * @param dependencies Piranha dependencies. * @param repositories Piranha repositories. * @param offline Offline flag. * @param port http port on which Piranha listens to requests. * @param rootIsWarName sets that the war name should be used for the root context * @param root the context root for web applications * @param httpServer the HTTP server implementation to use. * @param httpStart whether or not to start the HTTP server. * @param extensionsList List of extensions. * @param repositoriesList List of repos. * @param mergedDependencies List of merged dependencies. */ public MicroConfiguration( String version, String extensions, String dependencies, String repositories, boolean offline, int port, boolean rootIsWarName, String root, String httpServer, boolean httpStart, List extensionsList, List repositoriesList, List mergedDependencies) { this.version = version; this.extensions = extensions; this.dependencies = dependencies; this.repositories = repositories; this.offline = offline; this.port = port; this.rootIsWarName = rootIsWarName; this.root = root; this.httpServer = httpServer; this.httpStart = httpStart; this.extensionsList = extensionsList; this.repositoriesList = repositoriesList; this.mergedDependencies = mergedDependencies; } /** * Handle post construct. * * @return the configuration. */ public MicroConfiguration postConstruct() { if (root != null) { if (root.equalsIgnoreCase("ROOT")) { root = ""; } else if (!root.startsWith("/")) { root = "/" + root; } } extensionsList = stream(extensions.split(",")) .map(String::trim) .toList(); Stream dependenciesFromExtensionsStream = extensionsList.stream() .map(extension -> "cloud.piranha.extension:piranha-extension-" + extension + ":" + version); Stream directDependenciesStream = stream(dependencies.split(",")) .map(String::trim) .filter(dep -> !dep.isEmpty()); repositoriesList = stream(repositories.split(",")) .map(String::trim) .filter(repo -> !repo.isEmpty()) .toList(); mergedDependencies = Stream.of( Stream.of("cloud.piranha.micro:piranha-micro-core:" + version), Stream.of("cloud.piranha.http:piranha-http-" + httpServer + ":" + version), dependenciesFromExtensionsStream, directDependenciesStream ).flatMap(identity()).toList(); return this; } /** * Construct a map for the configuration. * * @return the map. */ public Map toMap() { Map config = new HashMap<>(); config.put("micro.port", getPort()); config.put("micro.rootIsWarName", rootIsWarName); if (getContextPath() != null) { config.put("micro.root", getContextPath()); } config.put("micro.http.start", httpStart); return config; } /** * {@return the version} */ public String getVersion() { return version; } /** * Set the version. * * @param version the version. */ public void setVersion(String version) { this.version = version; } /** * {@return the extensions} */ public String getExtensions() { return extensions; } /** * Set the extensions. * * @param extensions the extensions. */ public void setExtensions(String extensions) { this.extensions = extensions; } /** * {@return the repositories} */ public String getRepositories() { return repositories; } /** * Set the repositories. * * @param repositories the repositories. */ public void setRepositories(String repositories) { this.repositories = repositories; } /** * Are we offline. * * @return true if so, false otherwise. */ public boolean isOffline() { return offline; } /** * Set the offline flag. * * @param offline the offline flag. */ public void setOffline(boolean offline) { this.offline = offline; } /** * {@return the port} */ public int getPort() { return port; } /** * Set the port. * * @param port the port. */ public void setPort(int port) { this.port = port; } /** * Whether the war name is used for the context root * * @return whether the war name is used for the context root */ public boolean isRootIsWarName() { return rootIsWarName; } /** * Sets that the war name should be used for the root context of a web app. * *

* Setting this to true overrides the explicit context root set via the setContextPath method. * * @param rootIsWarName whether the war name is used for the context root */ public void setRootIsWarName(boolean rootIsWarName) { this.rootIsWarName = rootIsWarName; } /** * {@return the context path} */ public String getContextPath() { return root; } /** * Set the context path. * * @param contextPath the context path. */ public void setContextPath(String contextPath) { this.root = contextPath; } /** * {@return the list of extensions} */ public List getExtensionsList() { return extensionsList; } /** * {@return the list of repositories} */ public List getRepositoriesList() { return repositoriesList; } /** * {@return the merged dependencies} */ public List getMergedDependencies() { return mergedDependencies; } /** * {@return the HTTP server engine} */ public String getHttpServer() { return httpServer; } /** * Set the HTTP server engine to use. * * @param httpServer the HTTP server engine. */ public void setHttpServer(String httpServer) { this.httpServer = httpServer; } /** * Checks whether the config asks to start the http server * * @return true if an http server will be started, false otherwise */ public boolean isHttpStart() { return httpStart; } /** * Sets whether the config asks to start the http server * * @param httpStart the HTTP start flag. */ public void setHttpStart(boolean httpStart) { this.httpStart = httpStart; } } ================================================ FILE: micro/loader/src/main/java/cloud/piranha/micro/loader/MicroDeployOutcome.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.micro.loader; import java.util.Map; import java.util.Set; import java.util.function.Consumer; /** * The Micro deploy outcome. * * @author Arjan Tijms */ public class MicroDeployOutcome { /** * Stores the deployed servlets. */ private Set deployedServlets; /** * Stores the deployed applications. */ private Consumer> deployedApplication; /** * The context root to which the application was deployed. This is the first * path after the the host. */ private String deployedContextRoot; /** * Constructor. */ public MicroDeployOutcome() { } /** * {@return the deploy outcome} * @param deployMap the deployed application. */ @SuppressWarnings("unchecked") public static MicroDeployOutcome ofMap(Map deployMap) { MicroDeployOutcome deployOutcome = new MicroDeployOutcome(); deployOutcome.setDeployedServlets((Set) deployMap.get("deployedServlets")); deployOutcome.setDeployedApplication((Consumer>) deployMap.get("deployedApplication")); deployOutcome.setDeployedContextRoot((String) deployMap.get("deployedContextRoot")); return deployOutcome; } /** * {@return the deployed servlets} */ public Set getDeployedServlets() { return deployedServlets; } /** * Set the deployed servlets. * * @param deployedServlets the deployed servlets. */ public void setDeployedServlets(Set deployedServlets) { this.deployedServlets = deployedServlets; } /** * Get the deployed application. * * @return the deployed application map. */ public Consumer> getDeployedApplication() { return deployedApplication; } /** * Set the deployed application. * * @param deployedApplication the deployed application. */ public void setDeployedApplication(Consumer> deployedApplication) { this.deployedApplication = deployedApplication; } /** * {@return the deployed context root} */ public String getDeployedContextRoot() { return deployedContextRoot; } /** * Set the deployed context root * * @param deployedContextRoot the deployed context root */ public void setDeployedContextRoot(String deployedContextRoot) { this.deployedContextRoot = deployedContextRoot; } } ================================================ FILE: micro/loader/src/main/java/cloud/piranha/micro/loader/MicroInfo.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.micro.loader; /** * The Piranha Micro information. * * @author Arjan Tijms */ public class MicroInfo { /** * Stores the platform constant */ private static final String PLATFORM = "micro"; /** * Constructor. */ public MicroInfo() { } /** * Get the platform. * * @return the platform, set info based on properties later */ public String getPlatform() { return PLATFORM; } } ================================================ FILE: micro/loader/src/main/java/cloud/piranha/micro/loader/MicroOuterDeployer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.micro.loader; import static java.util.Objects.requireNonNull; import static java.lang.System.Logger.Level.WARNING; import static org.jboss.shrinkwrap.resolver.api.maven.repository.MavenUpdatePolicy.UPDATE_POLICY_NEVER; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.module.Configuration; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleFinder; import java.lang.module.ModuleReference; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.util.List; import java.util.Map; import java.util.UUID; import java.lang.System.Logger; import java.util.stream.Stream; import org.jboss.jandex.Index; import org.jboss.jandex.IndexWriter; import org.jboss.jandex.Indexer; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.ByteArrayAsset; import org.jboss.shrinkwrap.api.asset.EmptyAsset; import org.jboss.shrinkwrap.api.exporter.ZipExporter; import org.jboss.shrinkwrap.api.importer.ZipImporter; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.jboss.shrinkwrap.resolver.api.maven.ConfigurableMavenResolverSystem; import org.jboss.shrinkwrap.resolver.api.maven.Maven; import org.jboss.shrinkwrap.resolver.api.maven.repository.MavenRemoteRepositories; import org.jboss.shrinkwrap.resolver.api.maven.repository.MavenRemoteRepository; import cloud.piranha.core.impl.DefaultModuleFinder; import cloud.piranha.core.impl.DefaultModuleLayerProcessor; import cloud.piranha.resource.api.Resource; import cloud.piranha.resource.impl.DefaultResourceManager; import cloud.piranha.resource.impl.DefaultResourceManagerClassLoader; import cloud.piranha.resource.impl.MultiReleaseResource; import cloud.piranha.resource.shrinkwrap.IsolatingResourceManagerClassLoader; import cloud.piranha.resource.shrinkwrap.ShrinkWrapResource; /** * The micro outer deployer runs in the outer (or initial) class loader, and * initializes the inner (isolated) class loader. * *

* Initialization consists of loading the required classes that make up the * requested configuration of Piranha Micro itself, and putting these in the * parent inner class loader, as well as indexing the application classes and * putting both this index and the application classes + resources in a child of * the parent inner class loader. * *

* These inner class loaders are then used to bootstrap the inner deployer, and * control is handed to it. The inner deployer full runs in the inner class * loader, and will startup an actual Piranha instance and deploy the given * archive to it. * * @author Arjan Tijms */ public class MicroOuterDeployer { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(MicroOuterDeployer.class.getName()); /** * Stores the configuration. */ private final MicroConfiguration configuration; /** * Stores the inner deployer. */ private Object microInnerDeployer; /** * Constructor. */ public MicroOuterDeployer() { this(new MicroConfiguration().postConstruct()); } /** * Constructor. * * @param configuration the configuration. */ public MicroOuterDeployer(MicroConfiguration configuration) { requireNonNull(configuration); this.configuration = configuration; } /** * Deploy the given archive. * * @param archive the archive. * @return the outcome. */ @SuppressWarnings("unchecked") public MicroDeployOutcome deploy(Archive archive) { if (!archive.contains("WEB-INF/beans.xml")) { archive.add(EmptyAsset.INSTANCE, "WEB-INF/beans.xml"); } if (archive instanceof WebArchive webArchive) { webArchive.addClass(MicroInfo.class); } ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); try { // Resolve all the dependencies that make up a Piranha runtime configuration ConfigurableMavenResolverSystem mavenResolver = Maven.configureResolver(); configuration.getRepositoriesList().stream().forEach(repoUrl -> mavenResolver.withRemoteRepo(createRepo(repoUrl))); JavaArchive[] piranhaArchives = mavenResolver .workOffline(configuration.isOffline()) .resolve(configuration.getMergedDependencies()) .withTransitivity() .as(JavaArchive.class); // Make all those dependencies available to the Piranha class loader ClassLoader piranhaClassLoader = getPiranhaClassLoader(piranhaArchives); // Make the web application archive (the .war) available to a separate classloader // The webInfClassLoader delegates to the Piranha class loader. // The class loading hierarchy looks as follows: // Web-inf class loader (application classes) // | // |--- System class loader (Pass-through for Shrinkwrap classes only) // |--- java.lang.ClassLoader (super class, Weld, Javasist etc hack-in their classes here) // | // Piranha class loader (Piranha classes) // | // | // Platform class loader (JDK classes) ClassLoader webInfClassLoader = getWebInfClassLoader(archive, piranhaClassLoader); Thread.currentThread().setContextClassLoader(webInfClassLoader); try { URL.setURLStreamHandlerFactory(new StaticURLStreamHandlerFactory()); } catch (Error error) { // Yes, we know... // Ignore } System.setProperty("micro.version", getClass().getPackage().getImplementationVersion()); if (Boolean.getBoolean("cloud.piranha.modular.enable")) { setupLayers((DefaultResourceManagerClassLoader) piranhaClassLoader, (DefaultResourceManagerClassLoader) webInfClassLoader); } microInnerDeployer = Class.forName( "cloud.piranha.micro.core.MicroInnerDeployer", true, webInfClassLoader) .getDeclaredConstructor() .newInstance(); return MicroDeployOutcome.ofMap((Map) microInnerDeployer .getClass() .getMethod("start", Archive.class, ClassLoader.class, Map.class, Map.class) .invoke(microInnerDeployer, archive, webInfClassLoader, StaticURLStreamHandlerFactory.getHandlers(), configuration.toMap())); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { throw new IllegalStateException("", e); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } private void setupLayers(DefaultResourceManagerClassLoader piranhaClassLoader, DefaultResourceManagerClassLoader webInfClassLoader) { // Need to improve this, it searching for the same modules in two module finders, // however we need this because as the classes are defined by two classloader // it needs to use the correct classloader to "attach" the module information List piranhaResources = piranhaClassLoader.getResourceManager().getResourceList(); List applicationResources = webInfClassLoader.getResourceManager().getResourceList(); DefaultModuleFinder piranhaLibsModuleFinder = new DefaultModuleFinder(piranhaResources); DefaultModuleFinder moduleFinder = new DefaultModuleFinder(Stream.concat(piranhaResources.stream(), applicationResources.stream()).toList()); List roots = moduleFinder.findAll().stream() .map(ModuleReference::descriptor) .map(ModuleDescriptor::name) .toList(); Configuration resolve = ModuleLayer.boot().configuration().resolveAndBind(moduleFinder, ModuleFinder.of(), roots); // Maps each module to the classloader ModuleLayer.Controller controller = ModuleLayer.defineModules(resolve, List.of(ModuleLayer.boot()), m -> piranhaLibsModuleFinder.find(m).isPresent() ? piranhaClassLoader : webInfClassLoader); ModuleLayer moduleLayer = controller.layer(); // Allow the module layer to read the classes from the unnamed module moduleLayer.findModule("cloud.piranha.resource.shrinkwrap").ifPresent(x -> controller.addReads(x, this.getClass().getModule())); moduleLayer.findModule("cloud.piranha.micro.core").ifPresent(x -> controller.addReads(x, this.getClass().getModule())); DefaultModuleLayerProcessor.INSTANCE.processModuleLayerOptions(controller); } /** * Stop the inner deployer. */ public void stop() { if (microInnerDeployer != null) { try { microInnerDeployer .getClass() .getMethod("stop") .invoke(microInnerDeployer); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { LOGGER.log(WARNING, "Error occurred during stop of Piranha Micro", e); } } } /** * Gets a class loader that provides access to the Piranha classes. * * @param piranhaArchives the archives containing code that makes up Piranha * @return A class loader giving access to the Piranha runtime code */ ClassLoader getPiranhaClassLoader(Archive[] piranhaArchives) { DefaultResourceManager manager = new DefaultResourceManager(); for (Archive archive : piranhaArchives) { manager.addResource(new MultiReleaseResource(new ShrinkWrapResource(archive))); } IsolatingResourceManagerClassLoader classLoader = new IsolatingResourceManagerClassLoader("Piranha Loader"); classLoader.setResourceManager(manager); return classLoader; } /** * Gets a class loader that provides access to the classes in WEB-INF of a * web application archive. * * @param applicationArchive the web application archive to provide access * to * @param piranhaClassloader the parent class loader containing the Piranha * runtime classes * @return A class loader giving access to the application code */ ClassLoader getWebInfClassLoader(Archive applicationArchive, ClassLoader piranhaClassloader) { // Create the resource that holds all classes from the WEB-INF/classes folder ShrinkWrapResource webInfClasses = new ShrinkWrapResource("/WEB-INF/classes", applicationArchive, "cloud.piranha.modular.classes"); addIndexToResource(webInfClasses); // Create the resources that hold all classes from the WEB-INF/lib folder. // Each resource holds the classes from a single jar ShrinkWrapResource jarResources = new ShrinkWrapResource("/WEB-INF/lib", applicationArchive, applicationArchive.getName() + ".libs"); List webInfLib = jarResources.getAllLocations() .filter(location -> location.endsWith(".jar")) .map(location -> importAsShrinkWrapResource(jarResources, location)) .toList(); webInfLib.stream() .filter(resource -> resource.getAllLocations() .anyMatch(location -> location.startsWith("/META-INF/resources"))) .forEach(resource -> applicationArchive.add(resource.getArchive(), "/META-INF/piranha/resource-libs", ZipExporter.class)); // Create a separate archive that contains an index of the application archive and the library archives. // This index can be obtained from the class loader by getting the "META-INF/piranha.idx" resource. ShrinkWrapResource indexResource = new ShrinkWrapResource( ShrinkWrap.create(JavaArchive.class, "piranha-jandex-module.jar") .add(new ByteArrayAsset(createIndex(webInfClasses, webInfLib)), "META-INF/piranha.idx")); IsolatingResourceManagerClassLoader classLoader = new IsolatingResourceManagerClassLoader(piranhaClassloader, "WebInf Loader"); // Add the resources representing the application archive and index archive to the resource manager DefaultResourceManager manager = new DefaultResourceManager(); manager.addResource(new MultiReleaseResource(webInfClasses)); for (ShrinkWrapResource webLibResource : webInfLib) { manager.addResource(new MultiReleaseResource(webLibResource)); } manager.addResource(indexResource); // Make the application and library classes, as well as the index available to the class loader by setting the resource manager // that contains these. classLoader.setResourceManager(manager); return classLoader; } /** * Helper method that gets and imports a ZipFileEntry resource from a * ShrinkWrapResource as another ShrinkWrapResource. * * @param resource the ShrinkWrapResource used as the source * @param location the location of the target resource within the resource * @return a ShrinkWrapResource version of the target resource */ private ShrinkWrapResource importAsShrinkWrapResource(ShrinkWrapResource resource, String location) { ShrinkWrapResource unzippedResource = new ShrinkWrapResource( ShrinkWrap.create(ZipImporter.class, location.substring(1)) .importFrom(resource.getResourceAsStreamByLocation(location)) .as(JavaArchive.class)); addIndexToResource(unzippedResource); return unzippedResource; } private void addIndexToResource(ShrinkWrapResource resource) { resource.getArchive() .add(new ByteArrayAsset(createIndexForResource(resource)), "META-INF/jandex.idx"); } private byte[] createIndexForResource(ShrinkWrapResource resource) { Indexer indexer = new Indexer(); addAllClassesToIndex(resource, indexer); Index index = indexer.complete(); // Write the index out to a byte array ByteArrayOutputStream indexBytes = new ByteArrayOutputStream(); IndexWriter writer = new IndexWriter(indexBytes); try { writer.write(index); } catch (IOException ioe) { LOGGER.log(WARNING, "Unable to write out index", ioe); } return indexBytes.toByteArray(); } private byte[] createIndex(ShrinkWrapResource applicationResource, List libResources) { Indexer indexer = new Indexer(); // Add all classes from the library resources (the jar files in WEB-INF/lib) libResources.stream() .forEach(libResource -> addAllClassesToIndex(libResource, indexer)); // Add all classes from the application resource (the class files in WEB-INF/classes to the indexer) // Note this must be done last as according to the Servlet spec, WEB-INF/classes overrides WEB-INF/lib) addAllClassesToIndex(applicationResource, indexer); Index index = indexer.complete(); // Write the index out to a byte array ByteArrayOutputStream indexBytes = new ByteArrayOutputStream(); IndexWriter writer = new IndexWriter(indexBytes); try { writer.write(index); } catch (IOException ioe) { LOGGER.log(WARNING, "Unable to write out index", ioe); } return indexBytes.toByteArray(); } private void addAllClassesToIndex(ShrinkWrapResource resource, Indexer indexer) { resource.getAllLocations() .filter(e -> e.endsWith(".class")) .forEach(className -> addSingleClassToIndex(className, resource, indexer)); } private void addSingleClassToIndex(String className, ShrinkWrapResource resource, Indexer indexer) { try (InputStream classAsStream = resource.getResourceAsStream(className)) { indexer.index(classAsStream); } catch (IOException ioe) { LOGGER.log(WARNING, "Unable to add to index", ioe); } } private MavenRemoteRepository createRepo(String repoUrl) { MavenRemoteRepository repo = MavenRemoteRepositories.createRemoteRepository( UUID.randomUUID().toString(), repoUrl, "default"); repo.setUpdatePolicy(UPDATE_POLICY_NEVER); return repo; } } ================================================ FILE: micro/loader/src/main/java/cloud/piranha/micro/loader/StaticStreamHandler.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.micro.loader; import java.io.IOException; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; import java.util.Map; import java.util.function.Function; /** * A static URL stream handler. * * @author Manfred Riem (mriem@manorrock.com) */ public class StaticStreamHandler extends URLStreamHandler { /** * Stores the protocol. */ private final String protocol; /** * Stores the handlers. */ private final Map> handlers; /** * Constructor. * * @param protocol the protocol. * @param handlers the handlers. */ public StaticStreamHandler(String protocol, Map> handlers) { this.protocol = protocol; this.handlers = handlers; } @Override protected URLConnection openConnection(URL u) throws IOException { return handlers.get(protocol).apply(u); } } ================================================ FILE: micro/loader/src/main/java/cloud/piranha/micro/loader/StaticURLStreamHandlerFactory.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.micro.loader; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; import java.net.URLStreamHandlerFactory; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; /** * A factory for URL stream handlers using a static map to contain handlers. * *

* This factory should be registered with the JVM early. Later on the HANDLERS map * can be used to register individual URL stream handlers for various protocols. * * @author Arjan Tijms */ public class StaticURLStreamHandlerFactory implements URLStreamHandlerFactory { /** * Stores the handlers. */ private static final Map> HANDLERS = new ConcurrentHashMap<>(); /** * Constructor. */ public StaticURLStreamHandlerFactory() { } /** * {@return the handlers} */ public static Map> getHandlers() { return HANDLERS; } @Override public URLStreamHandler createURLStreamHandler(String protocol) { if (!HANDLERS.containsKey(protocol)) { return null; } return new StaticStreamHandler(protocol, HANDLERS); } } ================================================ FILE: micro/loader/src/main/java/cloud/piranha/micro/loader/package-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** *

* This package delivers you with a Servlet container that hosts only a single * application. *

* *

Architecture diagram

* *

* The image below illustrates how the request and response handling is done by * Piranha Micro. When a request comes in to the HTTP server it dispatches it to * the WebApplicationServer which in turn * dispatches it to the WebApplication which then in turn uses * WebApplicationRequestMapper to determine * which FilterChain needs to process the incoming request and it dispatches to * it. *

* *

* Request and response handling *

* *

How do I use Piranha Micro?

* *

* See our documentation for more * information. *

* * @author Manfred Riem (mriem@manorrock.com) */ package cloud.piranha.micro.loader; ================================================ FILE: micro/loader/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the loader for Piranha Micro. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.micro.loader { exports cloud.piranha.micro.loader; opens cloud.piranha.micro.loader; requires cloud.piranha.core.impl; requires cloud.piranha.resource.shrinkwrap; requires static java.net.http; requires org.jboss.jandex; requires shrinkwrap.api; requires shrinkwrap.resolver.api.maven; } ================================================ FILE: micro/pom.xml ================================================ 4.0.0 cloud.piranha project 25.4.0-SNAPSHOT cloud.piranha.micro project pom Piranha - Micro builder core loader cloud.piranha bom ${project.version} pom import default file:///tmp/piranha/micro/ ================================================ FILE: multi/pom.xml ================================================ 4.0.0 cloud.piranha project 25.4.0-SNAPSHOT cloud.piranha piranha-multi jar Piranha - Multi cloud.piranha.feature piranha-feature-exitonstop ${project.version} compile cloud.piranha.feature piranha-feature-http ${project.version} compile cloud.piranha.feature piranha-feature-https ${project.version} compile cloud.piranha.feature piranha-feature-logging ${project.version} compile cloud.piranha.feature piranha-feature-webapps ${project.version} compile org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test piranha-multi org.codehaus.mojo build-helper-maven-plugin reserve-network-port reserve-network-port validate httpPort httpPort2 httpsPort2 org.apache.maven.plugins maven-assembly-plugin package single false src/main/assembly/zip.xml org.apache.maven.plugins maven-failsafe-plugin integration-test verify org.apache.maven.plugins maven-jar-plugin true cloud.piranha.multi.MultiPiranhaMain org.apache.maven.plugins maven-surefire-plugin ${project.build.directory} ${httpPort} ${httpPort2} ${httpsPort2} ================================================ FILE: multi/src/main/assembly/zip.xml ================================================ zip piranha zip tar.gz ${project.basedir}/src/main/zip ${project.build.directory}/webapps webapps ${project.build.directory}/piranha-multi.jar lib ${project.groupId}:${project.artifactId}:jar:* lib true ================================================ FILE: multi/src/main/docker/Dockerfile ================================================ FROM eclipse-temurin:21 RUN useradd -m piranha ADD target/piranha-multi.tar.gz /home/piranha/ RUN chown -R piranha:piranha /home/piranha WORKDIR /home/piranha/piranha/bin USER piranha CMD ["/bin/bash", "./run.sh"] ================================================ FILE: multi/src/main/java/cloud/piranha/multi/MultiPiranha.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.multi; import cloud.piranha.core.api.Piranha; import cloud.piranha.core.api.PiranhaConfiguration; import cloud.piranha.core.api.WebApplicationExtension; import cloud.piranha.core.impl.DefaultPiranhaConfiguration; import cloud.piranha.feature.api.FeatureManager; import cloud.piranha.feature.exitonstop.ExitOnStopFeature; import cloud.piranha.feature.http.HttpFeature; import cloud.piranha.feature.https.HttpsFeature; import cloud.piranha.feature.impl.DefaultFeatureManager; import cloud.piranha.feature.logging.LoggingFeature; import cloud.piranha.feature.webapps.WebAppsFeature; import cloud.piranha.http.api.HttpServer; import java.io.File; import java.io.IOException; import java.lang.System.Logger; import static java.lang.System.Logger.Level.INFO; import static java.lang.System.Logger.Level.WARNING; import java.nio.file.Files; /** * The Multi version of Piranha. * *

* This version of Piranha supports the following: *

*
    *
  1. Running with Java modules
  2. *
  3. Exiting on stop
  4. *
  5. Exposing a HTTP endpoint
  6. *
  7. Exposing a HTTPS endpoint
  8. *
  9. Setting the logging level
  10. *
  11. Hosting multiple web application
  12. *
* * @author Manfred Riem (mriem@manorrock.com) */ public class MultiPiranha implements Piranha, Runnable { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(MultiPiranha.class.getName()); /** * Stores the 'tmp/piranha.pid' file constant. */ private static final String PID_FILE = "tmp/piranha.pid"; /** * Stores the configuration. */ private final PiranhaConfiguration configuration; /** * Stores the feature manager. */ private final FeatureManager featureManager; /** * Stores the HTTP feature. */ private HttpFeature httpFeature; /** * Stores the HTTP server. */ private HttpServer httpServer; /** * Stores the HTTP feature. */ private HttpsFeature httpsFeature; /** * Stores the HTTP server. */ private HttpServer httpsServer; /** * Stores the started flag. */ private boolean started = false; /** * Stores the thread we use. */ private Thread thread; /** * Stores the WebAppsFeature. */ private WebAppsFeature webAppsFeature; /** * Constructor. */ public MultiPiranha() { configuration = new DefaultPiranhaConfiguration(); configuration.setBoolean("exitOnStop", false); configuration.setInteger("httpPort", 8080); configuration.setInteger("httpsPort", -1); configuration.setBoolean("jpmsEnabled", false); configuration.setFile("webAppsDir", new File("webapps")); featureManager = new DefaultFeatureManager(); } @Override public PiranhaConfiguration getConfiguration() { return configuration; } /** * Are we running? * * @return true if we are, false otherwise. */ private boolean isRunning() { boolean result = false; if (httpServer != null) { result = httpServer.isRunning(); } else if (httpsServer != null) { result = httpsServer.isRunning(); } return result; } /** * Have we started? * * @return true if we have, false otherwise. */ private boolean isStarted() { return started; } /** * Run method. */ @SuppressWarnings("unchecked") @Override public void run() { long startTime = System.currentTimeMillis(); LoggingFeature loggingFeature = new LoggingFeature(); featureManager.addFeature(loggingFeature); loggingFeature.setLevel(configuration.getString("loggingLevel")); loggingFeature.init(); loggingFeature.start(); LOGGER.log(INFO, () -> "Starting Piranha"); webAppsFeature = new WebAppsFeature(); featureManager.addFeature(webAppsFeature); webAppsFeature.setExtensionClass((Class) configuration.getClass("extensionClass")); webAppsFeature.setJpmsEnabled(configuration.getBoolean("jpmsEnabled", false)); webAppsFeature.setWebAppsDir(configuration.getFile("webAppsDir")); webAppsFeature.init(); webAppsFeature.start(); /* * Construct, initialize and start HTTP endpoint (if applicable). */ if (configuration.getInteger("httpPort") > 0) { httpFeature = new HttpFeature(); httpFeature.setHttpServerClass(configuration.getString("httpServerClass")); httpFeature.setPort(configuration.getInteger("httpPort")); httpFeature.init(); httpFeature.getHttpServer().setHttpServerProcessor(webAppsFeature.getHttpServerProcessor()); httpFeature.start(); httpServer = httpFeature.getHttpServer(); } /* * Construct, initialize and start HTTPS endpoint (if applicable). */ if (configuration.getInteger("httpsPort") > 0) { httpsFeature = new HttpsFeature(); httpsFeature.setHttpsKeystoreFile(configuration.getString("httpsKeystoreFile")); httpsFeature.setHttpsKeystorePassword(configuration.getString("httpsKeystorePassword")); httpsFeature.setHttpsServerClass(configuration.getString("httpsServerClass")); httpsFeature.setHttpsTruststoreFile(configuration.getString("httpsTruststoreFile")); httpsFeature.setHttpsTruststorePassword(configuration.getString("httpsTruststorePassword")); httpsFeature.setPort(configuration.getInteger("httpsPort")); httpsFeature.init(); httpsFeature.getHttpsServer().setHttpServerProcessor(webAppsFeature.getHttpServerProcessor()); httpsFeature.start(); httpServer = httpsFeature.getHttpsServer(); } if (configuration.getBoolean("exitOnStop", false)) { ExitOnStopFeature exitOnStopFeature = new ExitOnStopFeature(); featureManager.addFeature(exitOnStopFeature); } long finishTime = System.currentTimeMillis(); LOGGER.log(INFO, "Started Piranha"); LOGGER.log(INFO, "It took {0} milliseconds", finishTime - startTime); started = true; File startedFile = new File("tmp/piranha.started"); File stoppedFile = new File("tmp/piranha.stopped"); if (stoppedFile.exists()) { try { Files.delete(stoppedFile.toPath()); } catch (IOException ioe) { LOGGER.log(WARNING, "Error while deleting existing piranha.stopped file", ioe); } } if (!startedFile.exists()) { try { startedFile.createNewFile(); } catch (IOException ioe) { LOGGER.log(WARNING, "Unable to create piranha.started file", ioe); } } File pidFile = new File(PID_FILE); while (isRunning()) { try { Thread.sleep(2000); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } if (!pidFile.exists()) { if (httpServer != null) { httpServer.stop(); } if (httpsServer != null) { httpsServer.stop(); } } } finishTime = System.currentTimeMillis(); LOGGER.log(INFO, "Stopped Piranha"); LOGGER.log(INFO, "We ran for {0} milliseconds", finishTime - startTime); if (startedFile.exists()) { try { Files.delete(startedFile.toPath()); } catch (IOException ioe) { LOGGER.log(WARNING, "Error while deleting existing piranha.started file", ioe); } } if (!stoppedFile.exists()) { try { stoppedFile.createNewFile(); } catch (IOException ioe) { LOGGER.log(WARNING, "Unable to create piranha.stopped file", ioe); } } featureManager.stop(); } /** * Set the web applications directory. * * @param webAppsDir the web applications directory. */ public void setWebAppsDir(File webAppsDir) { this.configuration.setFile("webAppsDir", webAppsDir); } /** * Start the server. */ public void start() { File pidFile = new File(PID_FILE); if (!pidFile.exists()) { try { if (!pidFile.getParentFile().exists()) { pidFile.getParentFile().mkdirs(); } pidFile.createNewFile(); } catch (IOException ex) { LOGGER.log(WARNING, "Unable to create PID file"); } } else { LOGGER.log(WARNING, "PID file already exists"); } thread = new Thread(this); thread.setDaemon(false); thread.start(); while (!isStarted()) { try { Thread.sleep(10); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } /** * Stop the server. */ public void stop() { File pidFile = new File(PID_FILE); if (pidFile.exists()) { try { Files.delete(pidFile.toPath()); } catch (IOException ioe) { LOGGER.log(WARNING, "Error occurred while deleting PID file", ioe); } } started = false; thread = null; } } ================================================ FILE: multi/src/main/java/cloud/piranha/multi/MultiPiranhaBuilder.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.multi; import cloud.piranha.core.api.PiranhaBuilder; import java.lang.System.Logger.Level; import static javax.naming.Context.INITIAL_CONTEXT_FACTORY; import cloud.piranha.core.api.WebApplicationExtension; import java.io.File; import static java.lang.System.Logger.Level.WARNING; /** * The builder so you can easily build instances of * {@link cloud.piranha.multi.MultiPiranha}. * * @author Manfred Riem (mriem@manorrock.com) * @see cloud.piranha.multi.MultiPiranha */ public class MultiPiranhaBuilder implements PiranhaBuilder { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(MultiPiranhaBuilder.class.getName()); /** * Stores the Piranha Server instance. */ private final MultiPiranha piranha = new MultiPiranha(); /** * Stores the InitialContext factory. */ private String initialContextFactory = "com.manorrock.herring.thread.ThreadInitialContextFactory"; /** * Stores the verbose flag. */ private boolean verbose = false; /** * Constructor. */ public MultiPiranhaBuilder() { } /** * Build the server. * * @return the server. */ @Override public MultiPiranha build() { if (verbose) { showArguments(); } System.setProperty(INITIAL_CONTEXT_FACTORY, initialContextFactory); return piranha; } /** * Set the exit on stop flag. * * @param exitOnStop the exit on stop flag. * @return the builder. */ public MultiPiranhaBuilder exitOnStop(boolean exitOnStop) { piranha.getConfiguration().setBoolean("exitOnStop", exitOnStop); return this; } /** * Set the extension class. * * @param extensionClass the extension class. * @return the builder. */ public MultiPiranhaBuilder extensionClass(Class extensionClass) { piranha.getConfiguration().setClass("extensionClass", extensionClass); return this; } /** * Set the extension class. * * @param extensionClassName the extension class name. * @return the builder. */ public MultiPiranhaBuilder extensionClass(String extensionClassName) { try { extensionClass(Class.forName(extensionClassName) .asSubclass(WebApplicationExtension.class)); } catch (ClassNotFoundException cnfe) { LOGGER.log(WARNING, "Unable to load default extension class", cnfe); } return this; } /** * Set the HTTP server port. * * @param httpPort the HTTP server port. * @return the builder. */ public MultiPiranhaBuilder httpPort(int httpPort) { piranha.getConfiguration().setInteger("httpPort", httpPort); return this; } /** * Set the HTTP server class. * * @param httpServerClass the HTTP server class. * @return the builder. */ public MultiPiranhaBuilder httpServerClass(String httpServerClass) { piranha.getConfiguration().setString("httpServerClass", httpServerClass); return this; } /** * Set the HTTPS keystore file. * * @param httpsKeystoreFile the HTTPS keystore file. * @return the builder. */ public MultiPiranhaBuilder httpsKeystoreFile(String httpsKeystoreFile) { piranha.getConfiguration().setString("httpsKeystoreFile", httpsKeystoreFile); return this; } /** * Set the HTTPS keystore password. * * @param httpsKeystorePassword the HTTPS keystore password. * @return the builder. */ public MultiPiranhaBuilder httpsKeystorePassword(String httpsKeystorePassword) { piranha.getConfiguration().setString("httpsKeystorePassword", httpsKeystorePassword); return this; } /** * Set the HTTPS server port. * * @param httpsPort the HTTPS server port. * @return the builder. */ public MultiPiranhaBuilder httpsPort(int httpsPort) { piranha.getConfiguration().setInteger("httpsPort", httpsPort); return this; } /** * Set the HTTPS server class. * * @param httpsServerClass the HTTPS server class. * @return the builder. */ public MultiPiranhaBuilder httpsServerClass(String httpsServerClass) { piranha.getConfiguration().setString("httpsServerClass", httpsServerClass); return this; } /** * Set the HTTPS truststore file. * * @param httpsTruststoreFile the HTTPS truststore file. * @return the builder. */ public MultiPiranhaBuilder httpsTruststoreFile(String httpsTruststoreFile) { piranha.getConfiguration().setString("httpsTruststoreFile", httpsTruststoreFile); return this; } /** * Set the HTTPS truststore password. * * @param httpsTruststorePassword the HTTPS truststore password. * @return the builder. */ public MultiPiranhaBuilder httpsTruststorePassword(String httpsTruststorePassword) { piranha.getConfiguration().setString("httpsTruststorePassword", httpsTruststorePassword); return this; } /** * Enable/disable JPMS. * * @param jpms the JPMS flag. * @return the builder. */ public MultiPiranhaBuilder jpms(boolean jpms) { piranha.getConfiguration().setBoolean("jpmsEnabled", jpms); return this; } /** * Set the logging level. * * @param loggingLevel the logging level. * @return the builder. */ public MultiPiranhaBuilder loggingLevel(String loggingLevel) { piranha.getConfiguration().setString("loggingLevel", loggingLevel); return this; } /** * Show the arguments used. */ private void showArguments() { LOGGER.log(Level.INFO, """ PIRANHA Arguments ========= Default extension class : %s Exit on stop : %s HTTP port : %s HTTP server class : %s HTTPS keystore file : %s HTTPS keystore password : **** HTTPS port : %s HTTPS server class : %s HTTPS truststore file : %s HTTPS truststore password : **** JPMS enabled : %s Logging level : %s Web applications dir : %s """.formatted( piranha.getConfiguration().getClass("extensionClass"), piranha.getConfiguration().getBoolean("exitOnStop", false), piranha.getConfiguration().getInteger("httpPort"), piranha.getConfiguration().getString("httpServerClass"), piranha.getConfiguration().getString("httpsKeystoreFile"), piranha.getConfiguration().getInteger("httpsPort"), piranha.getConfiguration().getString("httpsServerClass"), piranha.getConfiguration().getString("httpsTruststoreFile"), piranha.getConfiguration().getBoolean("jpmsEnabled", false), piranha.getConfiguration().getString("loggingLevel"), piranha.getConfiguration().getFile("webAppsDir") )); } /** * Set the verbose flag. * * @param verbose the verbose flag. * @return the builder. */ public MultiPiranhaBuilder verbose(boolean verbose) { this.verbose = verbose; return this; } /** * Set the web applications directory. * * @param webAppsDir the web applications directory. * @return the builder. */ public MultiPiranhaBuilder webAppsDir(String webAppsDir) { if (webAppsDir != null) { piranha.getConfiguration().setFile("webAppsDir", new File(webAppsDir)); } return this; } } ================================================ FILE: multi/src/main/java/cloud/piranha/multi/MultiPiranhaMain.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.multi; import java.lang.System.Logger.Level; /** * The Main for Piranha Multi. * * @author Manfred Riem (mriem@manorrock.com) */ public class MultiPiranhaMain { /** * Stores the logger */ private static final System.Logger LOGGER = System.getLogger(MultiPiranhaMain.class.getName()); /** * Constructor. */ public MultiPiranhaMain() { } /** * Main method. * * @param arguments the arguments. */ public static void main(String[] arguments) { MultiPiranhaBuilder builder = new MultiPiranhaMain().processArguments(arguments); if (builder != null) { builder.build().start(); } else { showHelp(); } } /** * Process the arguments. * * @param arguments the arguments. * @return the builder. */ protected MultiPiranhaBuilder processArguments(String[] arguments) { MultiPiranhaBuilder builder = new MultiPiranhaBuilder() .exitOnStop(true); if (arguments != null) { for (int i = 0; i < arguments.length; i++) { if (arguments[i].equals("--extension-class")) { builder = builder.extensionClass(arguments[i + 1]); } if (arguments[i].equals("--help")) { return null; } if (arguments[i].equals("--http-port")) { builder = builder.httpPort(Integer.parseInt(arguments[i + 1])); } if (arguments[i].equals("--http-server-class")) { builder = builder.httpServerClass(arguments[i + 1]); } if (arguments[i].equals("--https-keystore-file")) { builder = builder.httpsKeystoreFile(arguments[i + 1]); } if (arguments[i].equals("--https-keystore-password")) { builder = builder.httpsKeystorePassword(arguments[i + 1]); } if (arguments[i].equals("--https-port")) { builder = builder.httpsPort(Integer.parseInt(arguments[i + 1])); } if (arguments[i].equals("--https-server-class")) { builder = builder.httpsServerClass(arguments[i + 1]); } if (arguments[i].equals("--https-truststore-file")) { builder = builder.httpsTruststoreFile(arguments[i + 1]); } if (arguments[i].equals("--https-truststore-password")) { builder = builder.httpsTruststorePassword(arguments[i + 1]); } if (arguments[i].equals("--jpms")) { builder = builder.jpms(true); } if (arguments[i].equals("--logging-level")) { builder = builder.loggingLevel(arguments[i + 1]); } if (arguments[i].equals("--verbose")) { builder = builder.verbose(true); } if (arguments[i].equals("--webapps-dir")) { builder = builder.webAppsDir(arguments[i + 1]); } } } return builder; } /** * Show help. */ protected static void showHelp() { LOGGER.log(Level.INFO, ""); LOGGER.log(Level.INFO, """ --extension-class - Set the extension to use --help - Show this help --http-port - Set the HTTP port (use -1 to disable) --http-server-class - Set the HTTP server class to use --https-keystore-file - Set the HTTPS keystore file (applies to the whole JVM) --https-keystore-password - Set the HTTPS keystore password (applies to the whole JVM) --https-port - Set the HTTPS port (disabled by default) --https-server-class - Set the HTTPS server class to use --https-truststore-file - Set the HTTPS keystore file (applies to the whole JVM) --https-truststore-password - Set the HTTPS keystore password (applies to the whole JVM) --jpms - Enable Java Platform Module System --logging-level - Set the logging level --verbose - Shows the runtime parameters --webapps-dir - Set the web applications directory """); } } ================================================ FILE: multi/src/main/java/cloud/piranha/multi/MultiWebApplication.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.multi; import cloud.piranha.core.api.WebApplicationServerRequestMapper; import cloud.piranha.core.impl.DefaultWebApplication; import jakarta.servlet.ServletContext; import java.util.Objects; /** * This web application supports finding other contexts using * {@link ServletContext#getContext(String)}. */ public class MultiWebApplication extends DefaultWebApplication { /** * Stores the request mapper. */ private final WebApplicationServerRequestMapper requestMapper; /** * Constructor. * * @param requestMapper the request mapper. */ public MultiWebApplication(WebApplicationServerRequestMapper requestMapper) { this.requestMapper = Objects.requireNonNull(requestMapper); } @Override public ServletContext getContext(String uripath) { return requestMapper.findMapping(uripath); } } ================================================ FILE: multi/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers Piranha Multi. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.multi { exports cloud.piranha.multi; opens cloud.piranha.multi; requires cloud.piranha.feature.exitonstop; requires cloud.piranha.feature.http; requires cloud.piranha.feature.https; requires cloud.piranha.feature.logging; requires cloud.piranha.feature.webapps; requires java.logging; requires java.naming; requires transitive cloud.piranha.core.api; requires transitive jakarta.servlet; } ================================================ FILE: multi/src/main/zip/etc/logging.properties ================================================ # # Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # # Logging handlers, by default the ConsoleHandler and the FileHandler are # enabled. If you want to add other handlers, see the java.util.logging # documentation for more information. # handlers = java.util.logging.ConsoleHandler java.util.logging.FileHandler # # Global log level # .level = INFO # # The configuration for the ConsoleHandler # # 1. The log level is set to INFO # 2. The formatter used is the SimpleFormatter # # IMPORTANT NOTE # # A Handler (in this case the ConsoleHandler) will only log up up to the level # specified for its Handler. So be default only INFO messages and higher will # show up in the log. E.g if you want FINEST messages to show for a particular # Logger you will have to set the level of the Handler to FINEST as well. # # java.util.logging.ConsoleHandler.level = INFO java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter # # The configuration for the FileHandler # # 1. The log level is set to FINEST # 2. The formatter used is the SimpleFormatter # 3. The size of each log file is 100 MB. # 4. The number of log files to keep is 10 # 5. The handler is set to append to an existing log file. # 6. The filename pattern is tmp/piranha-%g.log java.util.logging.FileHandler.level = FINEST java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter java.util.logging.FileHandler.limit = 10485760 java.util.logging.FileHandler.count = 10 java.util.logging.FileHandler.append = true java.util.logging.FileHandler.pattern = tmp/piranha-%g.log ================================================ FILE: multi/src/main/zip/tmp/README ================================================ README ------ This directory is used for temporary files by Piranha. If the server is NOT running you can safely delete the contents of the directory, but do NOT delete the directory itself. ================================================ FILE: multi/src/main/zip/webapps/README ================================================ README ------ This directory is used for your web applications. If a file ends with the .war extension, or it is a sub directory it will be considered a web application, anything else will be ignored. ================================================ FILE: multi/src/test/java/cloud/piranha/multi/MultiPiranhaBuilderTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.multi; import java.net.ConnectException; import java.net.Socket; import javax.net.SocketFactory; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; /** * The JUnit tests for the MultiPiranhaBuilder class. * * @author Manfred Riem (mriem@manorrock.com) */ class MultiPiranhaBuilderTest { /** * Test httpPort method. * * @throws Exception when a serious error occurs. */ @Test void testHttpPort() throws Exception { MultiPiranha piranha = new MultiPiranhaBuilder() .httpPort(Integer.parseInt(System.getProperty("httpPort"))) .build(); piranha.start(); Thread.sleep(5000); try ( Socket socket = new Socket("localhost", Integer.parseInt(System.getProperty("httpPort")))) { assertNotNull(socket.getOutputStream()); } piranha.stop(); Thread.sleep(5000); } /** * Test httpsPort method. * * @throws Exception when a serious error occurs. */ @Test void testHttpPort2() throws Exception { MultiPiranha piranha = new MultiPiranhaBuilder() .httpPort(-1) .httpsPort(Integer.parseInt(System.getProperty("httpsPort2"))) .build(); piranha.start(); Thread.sleep(5000); try ( Socket socket = new Socket("localhost", Integer.parseInt(System.getProperty("httpPort2")))) { fail(); } catch (ConnectException e) { } piranha.stop(); Thread.sleep(5000); } /** * Test httpsPort method. * * @throws Exception when a serious error occurs. */ @Test void testHttpsPort2() throws Exception { MultiPiranha piranha = new MultiPiranhaBuilder() .httpsKeystoreFile("src/main/zip/etc/keystore.jks") .httpsKeystorePassword("password") .httpPort(8228) .httpsPort(8338) .build(); piranha.start(); Thread.sleep(5000); SocketFactory factory = SSLSocketFactory.getDefault(); try ( SSLSocket socket = (SSLSocket) factory.createSocket("localhost", 8338)) { assertNotNull(socket.getOutputStream()); assertNotNull(socket.getSSLParameters()); assertEquals("TLSv1.3", socket.getSSLParameters().getProtocols()[0]); } piranha.stop(); Thread.sleep(5000); } /** * Test defaultExtensionClass method. * * @throws Exception when a serious error occurs. */ @Test void testDefaultExtensionClass() throws Exception { MultiPiranha piranha = new MultiPiranhaBuilder() .httpPort(8080) .verbose(true) .build(); piranha.start(); Thread.sleep(5000); try ( Socket socket = new Socket("localhost", 8080)) { assertNotNull(socket.getOutputStream()); } catch (ConnectException e) { } piranha.stop(); Thread.sleep(5000); } } ================================================ FILE: multi/src/test/java/cloud/piranha/multi/MultiPiranhaIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.multi; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit integration tests for ServerPiranha class. * * @author Manfred Riem (mriem@manorrock.com) */ class MultiPiranhaIT { /** * Extract the zip input stream. * * @param zipInput the zip input stream. * @param filePath the file path. * @throws IOException when an I/O error occurs. */ private void extractZipInputStream(ZipInputStream zipInput, String filePath) throws IOException { try (BufferedOutputStream bufferOutput = new BufferedOutputStream(new FileOutputStream(filePath))) { byte[] bytesIn = new byte[8192]; int read; while ((read = zipInput.read(bytesIn)) != -1) { bufferOutput.write(bytesIn, 0, read); } } } /** * Extract the Server zip file. */ private void extractServer(File zipFile) { try (ZipInputStream zipInput = new ZipInputStream(new FileInputStream(zipFile))) { ZipEntry entry = zipInput.getNextEntry(); while (entry != null) { String filePath = "target" + File.separatorChar + entry.getName(); if (!entry.isDirectory()) { File file = new File(filePath); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } extractZipInputStream(zipInput, filePath); } zipInput.closeEntry(); entry = zipInput.getNextEntry(); } } catch (IOException ioe) { } } /** * Test run method. * * @throws Exception when a serious error occurs. */ @Test void testRun() throws Exception { extractServer(new File("target/piranha-multi.zip")); ProcessBuilder builder = new ProcessBuilder(); Process process; if (System.getProperty("os.name").toLowerCase().contains("windows")) { process = builder. directory(new File("target/piranha/bin")). command("cmd", "/c", "start.cmd"). start(); } else { process = builder. directory(new File("target/piranha/bin")). command("sh", "./start.sh"). start(); } process.waitFor(5, TimeUnit.SECONDS); Thread.sleep(5000); File pidFile = new File("target/piranha/tmp/piranha.pid"); assertTrue(pidFile.exists()); if (System.getProperty("os.name").toLowerCase().contains("windows")) { process = builder. directory(new File("target/piranha/bin")). command("cmd", "/c", "stop.cmd"). start(); } else { process = builder. directory(new File("target/piranha/bin")). command("sh", "./stop.sh"). start(); } process.waitFor(5, TimeUnit.SECONDS); Thread.sleep(5000); assertFalse(pidFile.exists()); } } ================================================ FILE: pom.xml ================================================ 4.0.0 cloud.piranha project 25.4.0-SNAPSHOT pom Piranha The Piranha Project delivers you with a variety of cloud containers and several useful add-on modules https://github.com/piranhacloud/piranha Piranha Cloud https://piranha.cloud BSD-3-Clause https://raw.githubusercontent.com/piranhacloud/piranha/current/LICENSE mriem Manfred Riem Manorrock.com atijms Arjan Tijms Thihup Thiago Henrique Hupner arquillian bom core debug dist embedded extension feature http maven multi micro resource single spring scm:git:git://github.com/piranhacloud/piranha.git scm:git:git@github.com:piranhacloud/piranha.git https://github.com/piranhacloud/piranha default file:///tmp/piranha/ ossrh https://oss.sonatype.org/content/repositories/snapshots 2.0.2 2.0.3 1.9.5.Final 10.0.0.Final 6.11.0 10.21.2 1.28.0 2.0.0-M4 3.1.0 0.1.3 5.0.0-B07 3.1.1 3.0.1 6.0.0 1.1.1 1.8 4.1.0-M1 2.0.0 33.4.8-jre 2.4.240 5.6.0 23.12.0 9.1.0.Final 4.17.0 3.5.0 4.0.6 1.3.1.Final 4.0.0-M2 3.0.1 2.3.0 1.11.4 5.11.4 3.15.2 3.9.11 3.15.1 3.1 2.1 3.0.1 4.1.4 4.2.8.Final 10.5 3.0.0 0.3 0.4 0.16 1.1.7 2.0.0-beta-2 2.0.0 3.3.3 4.0.2 3.5.7 2.0.1 1.0.1 2.2.0 2.3.21.Final 4.0.0 6.0.3.Final 3.0.4 3.6.1 2.8 0.43.4 1.7.2 0.8.13 1.0.34 1.0.0 3.2.0 3.7.1 3.6.0 3.5.0 3.14.0 3.9.0 3.1.4 3.6.2 3.5.3 3.2.8 3.1.4 3.9.1 3.4.2 3.11.3 3.15.1 3.15.2 1.5.3 3.3.1 3.6.1 3.21.0 3.4.0 3.5.4 3.5.0 1.0.0.RC2 21.2.0 1.7.0 1.4.0 2.20.1 21 UTF-8 org.apache.commons commons-compress ${commons-compress.version} org.apache.commons commons-fileupload2-jakarta-servlet6 ${commons-fileupload2-jakarta-servlet6.version} org.apache.maven maven-plugin-api ${maven-plugin-api.version} org.apache.maven.plugin-tools maven-plugin-annotations ${maven-plugin-annotations.version} org.apache.maven.plugin-tools maven-plugin-tools-annotations ${maven-plugin-tools-annotations.version} org.glassfish.concurro concurro ${concurro.version} org.eclipse.angus angus-mail ${angus.mail.version} org.eclipse.angus angus-activation ${angus.activation.version} org.glassfish.expressly expressly ${expressly.version} org.glassfish.epicyro epicyro ${epicyro.version} org.glassfish.exousia exousia ${exousia.version} org.glassfish.grizzly grizzly-http-server ${grizzly.version} org.glassfish.grizzly grizzly-http2 ${grizzly.version} org.glassfish.grizzly grizzly-npn-api ${grizzly-npn-api.version} org.glassfish.jaxb jaxb-runtime ${jaxb-runtime.version} org.glassfish.jersey.containers jersey-container-servlet ${jersey.version} org.glassfish.jersey.containers jersey-container-servlet-core ${jersey.version} org.glassfish.jersey.core jersey-common ${jersey.version} org.glassfish.jersey.ext.cdi jersey-cdi1x ${jersey.version} org.glassfish.jersey.inject jersey-hk2 ${jersey.version} org.glassfish.jersey.media jersey-media-json-binding ${jersey.version} org.glassfish.jersey.media jersey-media-json-processing ${jersey.version} org.glassfish.jersey.media jersey-media-sse ${jersey.version} org.glassfish.web jakarta.servlet.jsp.jstl ${jstl.version} org.eclipse.persistence eclipselink ${eclipselink.version} org.eclipse.persistence org.eclipse.persistence.core ${eclipselink.version} org.eclipse.persistence org.eclipse.persistence.jpa ${eclipselink.version} org.eclipse.persistence org.eclipse.persistence.json ${eclipselink.version} org.glassfish jakarta.faces ${mojarra.version} org.eclipse.parsson parsson ${parsson.version} org.glassfish.soteria soteria.spi.bean.decorator.weld ${soteria.version} org.glassfish.soteria soteria ${soteria.version} org.glassfish.tyrus tyrus-container-servlet ${tyrus.version} org.glassfish.wasp wasp ${wasp.version} org.eclipse yasson ${yasson.version} me.alexpanov free-port-finder ${free-port-finder.version} com.google.guava guava ${guava.version} com.h2database h2 ${h2.version} com.hazelcast hazelcast ${hazelcast.version} org.hibernate.validator hibernate-validator ${hibernate-validator.version} org.hibernate.validator hibernate-validator-cdi ${hibernate-validator.version} org.htmlunit htmlunit ${htmlunit.version} org.jboss.arquillian arquillian-bom ${arquillian.version} org.jboss.arquillian arquillian-build ${arquillian.version} org.jboss.classfilewriter jboss-classfilewriter ${jboss-classfilewriter.version} org.jboss.shrinkwrap shrinkwrap-impl-base ${shrinkwrap-impl-base.version} org.jboss.shrinkwrap.descriptors shrinkwrap-descriptors-api-base ${shrinkwrap-descriptors-api-base.version} org.jboss.shrinkwrap.resolver shrinkwrap-resolver-api-maven ${shrinkwrap-resolver.version} org.jboss.shrinkwrap.resolver shrinkwrap-resolver-depchain ${shrinkwrap-resolver.version} org.jboss.shrinkwrap.resolver shrinkwrap-resolver-impl-maven ${shrinkwrap-resolver.version} org.jboss.shrinkwrap.resolver shrinkwrap-resolver-impl-maven-archive ${shrinkwrap-resolver.version} org.jboss.shrinkwrap.resolver shrinkwrap-resolver-spi ${shrinkwrap-resolver.version} org.jboss.weld.servlet weld-servlet-core ${weld.version} org.junit.jupiter junit-jupiter ${junit.version} org.junit.jupiter junit-jupiter-api ${junit.version} org.junit.jupiter junit-jupiter-engine ${junit.version} org.junit.jupiter junit-jupiter-params ${junit.version} org.junit.platform junit-platform-launcher ${junit-platform-launcher.version} org.junit.vintage junit-vintage-engine ${junit.version} org.junit-pioneer junit-pioneer ${junit-pioneer.version} com.manorrock.herring herring ${herring.version} com.manorrock.herring herring-thread ${herring.version} com.nimbusds nimbus-jose-jwt ${nimbus-jose-jwt.version} io.netty netty-buffer ${netty.version} io.netty netty-codec-http ${netty.version} io.netty netty-codec-http2 ${netty.version} io.netty netty-transport ${netty.version} org.omnifaces omni-mp-config ${omni-mp-config.version} org.omnifaces microprofile-jwt-auth ${microprofile-jwt-auth.version} org.omnifaces omniutils ${omniutils.version} org.omnifaces omniservices ${omniservices.version} ee.omnifish omnibeans ${omnibeans.version} ee.omnifish transact-cdi-beans ${transact-cdi-beans.version} io.github.crac org-crac ${crac.version} io.smallrye jandex ${jandex.version} org.testcontainers testcontainers ${testcontainers.version} io.undertow undertow-core ${undertow.version} com.google.code.maven-replacer-plugin replacer ${maven-replacer-plugin.version} io.github.wiverson jtoolprovider-plugin ${jtoolprovider-plugin.version} org.apache.maven.plugins maven-antrun-plugin ${maven-antrun-plugin.version} org.apache.maven.plugins maven-assembly-plugin ${maven-assembly-plugin.version} org.apache.maven.plugins maven-checkstyle-plugin ${maven-checkstyle-plugin.version} com.puppycrawl.tools checkstyle ${checkstyle.version} org.apache.maven.plugins maven-clean-plugin ${maven-clean-plugin.version} org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} org.apache.maven.plugins maven-dependency-plugin ${maven-dependency-plugin.version} org.apache.maven.plugins maven-deploy-plugin ${maven-deploy-plugin.version} org.apache.maven.plugins maven-enforcer-plugin ${maven-enforcer-plugin.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} org.apache.maven.plugins maven-gpg-plugin ${maven-gpg-plugin.version} org.apache.maven.plugins maven-install-plugin ${maven-install-plugin.version} org.apache.maven.plugins maven-jar-plugin ${maven-jar-plugin.version} org.apache.maven.plugins maven-invoker-plugin ${maven-invoker-plugin.version} org.apache.maven.plugins maven-javadoc-plugin ${maven-javadoc-plugin.version} org.apache.maven.plugins maven-plugin-plugin ${maven-plugin-plugin.version} org.apache.maven.plugins maven-plugin-report-plugin ${maven-plugin-report-plugin.version} org.apache.maven.plugins maven-resources-plugin ${maven-resources-plugin.version} org.apache.maven.plugins maven-shade-plugin ${maven-shade-plugin.version} org.apache.maven.plugins maven-site-plugin ${maven-site-plugin.version} org.apache.maven.plugins maven-source-plugin ${maven-source-plugin.version} org.apache.maven.plugins maven-surefire-plugin ${maven-surefire-plugin.version} org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} org.codehaus.mojo build-helper-maven-plugin ${build-helper-maven-plugin.version} org.codehaus.mojo clirr-maven-plugin ${clirr-maven-plugin.version} org.codehaus.mojo flatten-maven-plugin ${flatten-maven-plugin.version} org.codehaus.mojo tidy-maven-plugin ${tidy-maven-plugin.version} org.codehaus.mojo versions-maven-plugin ${versions-maven-plugin.version} org.graalvm.nativeimage native-image-maven-plugin ${native-image-maven-plugin.version} org.jacoco jacoco-maven-plugin ${jacoco-maven-plugin.version} org.moditect moditect-maven-plugin ${moditect-maven-plugin.version} org.sonatype.plugins nexus-staging-maven-plugin ${nexus-staging-maven-plugin.version} org.springframework.boot spring-boot-maven-plugin ${spring-boot.version} org.apache.maven.plugins maven-checkstyle-plugin true true UTF-8 false **/module-info.java validate validate check org.codehaus.mojo clirr-maven-plugin org.apache.bcel bcel ${bcel.version} org.apache.maven.plugins maven-compiler-plugin true ${java.version} org.apache.maven.plugins maven-enforcer-plugin enforce-maven enforce 3.8.7 ${java.version} org.apache.maven.plugins maven-failsafe-plugin integration-test verify org.apache.maven.plugins maven-jar-plugin true true true ${maven.build.timestamp} ${project.version} https://raw.githubusercontent.com/piranhacloud/piranha/current/LICENSE org.apache.maven.plugins maven-javadoc-plugin attach-javadocs jar true -html5 true aggregate aggregate -html5 true ${project.build.directory}/staging org.codehaus.mojo versions-maven-plugin com.hazelcast hazelcast 5.5.0 io.netty range [4.2.0.Alpha1,) org.apache.maven maven-plugin-api range [4.0.0-alpha-1,) org.apache.maven.plugins range [4.0.0-beta-1,) org.apache.maven.plugin-tools range [4.0.0-beta-1,) org.glassfish.jersey.containers 4.0-M1-GF1 org.moditect moditect-maven-plugin range [1.0.0.RC3,1.2.2.Final] org.apache.maven.plugins maven-source-plugin attach-sources jar org.apache.maven.plugins maven-surefire-plugin true false org.apache.maven.plugins maven-war-plugin true false false false org.codehaus.mojo tidy-maven-plugin org.jacoco jacoco-maven-plugin true com/gargoylesoftware/htmlunit/** default-prepare-agent prepare-agent default-report prepare-package report org.sonatype.plugins nexus-staging-maven-plugin true ossrh https://oss.sonatype.org/ true org.apache.maven.plugins maven-project-info-reports-plugin 3.9.0 true index docker docker eclipse m2e.version org.eclipse.m2e lifecycle-mapping ${lifecycle-mapping-plugin.version} org.apache.maven.plugins maven-checkstyle-plugin [3.1.1,) check org.codehaus.mojo flatten-maven-plugin [1.2.5,) flatten com.google.code.maven-replacer-plugin replacer [1.5.3,) replace org.apache.maven.plugins maven-dependency-plugin [3.1.2,) unpack org.apache.maven.plugins maven-plugin-plugin [3.6.0,) descriptor release org.apache.maven.plugins maven-gpg-plugin sign-artifacts verify sign --pinentry-mode loopback test true test sonatype-nexus-staging Sonatype Nexus Staging https://jakarta.oss.sonatype.org/content/repositories/staging/ true false ================================================ FILE: resource/api/pom.xml ================================================ 4.0.0 cloud.piranha.resource project 25.4.0-SNAPSHOT piranha-resource-api jar Piranha - Resource - API ================================================ FILE: resource/api/src/main/java/cloud/piranha/resource/api/Resource.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.api; import java.io.InputStream; import java.net.URL; import java.util.stream.Stream; /** * The Resource API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface Resource { /** * Get the resource. * * @param location the location. * @return the URL. */ URL getResource(String location); /** * Get the resource as a stream. * * @param location the location. * @return the resource as a stream, or null if not found. */ InputStream getResourceAsStream(String location); /** * {@return all the locations for this resource} */ Stream getAllLocations(); /** * Get the name of this resource * @return the name */ default String getName() { return ""; } } ================================================ FILE: resource/api/src/main/java/cloud/piranha/resource/api/ResourceManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.api; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.Collection; import java.util.List; import java.util.stream.Stream; /** * The ResourceManager API. * * @author Manfred Riem (mriem@manorrock.com) */ public interface ResourceManager { /** * Add the resource. * * @param resource the resource. */ void addResource(Resource resource); /** * Get the resource. * * @param location the location. * @return the URL. * @throws MalformedURLException when the location is malformed. */ URL getResource(String location) throws MalformedURLException; /** * Get the resources. * * @param location the location * @return the URLs. * @throws MalformedURLException when the location is malformed. */ Collection getResources(String location) throws MalformedURLException; /** * Get the resource as a stream. * * @param location the location. * @return the input stream, or null if not found. */ InputStream getResourceAsStream(String location); /** * {@return all the locations for this resource manager} */ Stream getAllLocations(); /** * {@return all the locations for this resource manager} */ List getResourceList(); /** * {@return isAlsoTryLoadFromClass} */ default boolean isAlsoTryLoadFromClass() { return true; } /** * Sets the alsoTryLoadFromClass flag * @param alsoTryLoadFromClass the alsoTryLoadFromClass flag */ default void setAlsoTryLoadFromClass(boolean alsoTryLoadFromClass) { } } ================================================ FILE: resource/api/src/main/java/cloud/piranha/resource/api/ResourceManagerClassLoader.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.api; /** * The WebApplicationClassLoader API. * * @author Manfred Riem (mriem@manorrock.com) * @author Arjan Tijms */ public interface ResourceManagerClassLoader { /** * {@return the resource manager} */ ResourceManager getResourceManager(); /** * Set the resource manager. * * @param resourceManager the resource manager. */ void setResourceManager(ResourceManager resourceManager); } ================================================ FILE: resource/api/src/main/java/cloud/piranha/resource/api/package-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** *

* The cloud.piranha.resource.api package delivers the Resource API classes. *

* * @author Manfred Riem (mriem@manorrock.com) */ package cloud.piranha.resource.api; ================================================ FILE: resource/api/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Resource API. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.resource.api { exports cloud.piranha.resource.api; opens cloud.piranha.resource.api; } ================================================ FILE: resource/impl/pom.xml ================================================ 4.0.0 cloud.piranha.resource project 25.4.0-SNAPSHOT piranha-resource-impl jar Piranha - Resource - Implementation cloud.piranha.resource piranha-resource-api ${project.version} compile ================================================ FILE: resource/impl/src/main/java/cloud/piranha/resource/impl/AliasedDirectoryResource.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import cloud.piranha.resource.api.Resource; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import static java.lang.System.Logger.Level.WARNING; import java.net.MalformedURLException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.function.Predicate; import java.util.stream.Stream; /** * The default AliasedDirectoryResource. * * @author Manfred Riem (mriem@manorrock.com) */ public class AliasedDirectoryResource implements Resource { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(AliasedDirectoryResource.class.getName()); /** * Stores the root directory. */ private File rootDirectory; /** * Stores the alias. */ private String alias; /** * Constructor. */ public AliasedDirectoryResource() { this.rootDirectory = null; this.alias = null; } /** * Constructor. * * @param rootDirectory the root directory. * @param alias the alias. */ public AliasedDirectoryResource(File rootDirectory, String alias) { this.rootDirectory = rootDirectory; this.alias = alias; } @Override public URL getResource(String location) { URL result = null; if (location.startsWith(alias)) { location = location.substring(alias.length()); File file = new File(rootDirectory, location); if (file.exists()) { try { result = file.toURI().toURL(); } catch (MalformedURLException mue) { LOGGER.log(WARNING, "Malformed URL for aliased directory resource", mue); } } } return result; } @Override public InputStream getResourceAsStream(String location) { InputStream result = null; if (location.startsWith(alias)) { location = location.substring(alias.length() + 1); File file = new File(rootDirectory, location); try { result = new FileInputStream(file); } catch (FileNotFoundException fnfe) { LOGGER.log(WARNING, "Unable to find aliased directory resource", fnfe); } } return result; } @Override public Stream getAllLocations() { try { Path rootPath = Paths.get(rootDirectory.toURI()); Path root = Paths.get("/"); return Files.walk(rootPath) .filter(Predicate.not(Files::isDirectory)) .map(rootPath::relativize) .map(root::resolve) .map(p -> p.toString().replace("\\", "/")); } catch (IOException e) { return Stream.empty(); } } /** * {@return the alias} */ public String getAlias() { return this.alias; } /** * {@return the root directory} */ public File getRootDirectory() { return this.rootDirectory; } /** * Set the alias. * * @param alias the alias. */ public void setAlias(String alias) { this.alias = alias; } /** * Set the root directory. * * @param rootDirectory the root directory. */ public void setRootDirectory(File rootDirectory) { this.rootDirectory = rootDirectory; } } ================================================ FILE: resource/impl/src/main/java/cloud/piranha/resource/impl/AliasedNamedResource.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import java.io.InputStream; import java.net.URL; import java.util.stream.Stream; import cloud.piranha.resource.api.Resource; /** * A {@link Resource} wrapper with a custom name */ public class AliasedNamedResource implements Resource { /** * Stores the resource */ private final Resource resource; /** * Stores the resource name */ private final String resourceName; /** * Constructor * @param resource the resource * @param resourceName the resource name */ public AliasedNamedResource(Resource resource, String resourceName) { this.resource = resource; this.resourceName = resourceName; } @Override public URL getResource(String location) { return resource.getResource(location); } @Override public InputStream getResourceAsStream(String location) { return resource.getResourceAsStream(location); } @Override public Stream getAllLocations() { return resource.getAllLocations(); } @Override public String getName() { return resourceName; } @Override public String toString() { return getName() + " " + super.toString(); } } ================================================ FILE: resource/impl/src/main/java/cloud/piranha/resource/impl/ByteArrayResource.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.stream.Stream; import cloud.piranha.resource.api.Resource; /** * The byte-array resource. * * @author Manfred Riem (mriem@manorrock.com) */ public class ByteArrayResource implements Resource { /** * Stores the 'bytes://' protocol. */ private static final String BYTES_PROTOCOL = "bytes://"; /** * Stores the byte-array. */ private final byte[] bytes; /** * Stores the location. */ private final String location; /** * Constructor. * * @param location the location. * @param bytes the byte-array. */ public ByteArrayResource(String location, byte[] bytes) { this.location = location; this.bytes = bytes; } /** * Get all locations. * * @return the locations. */ @Override public Stream getAllLocations() { return Stream.of(location); } /** * {@return the byte-array} */ public byte[] getBytes() { return bytes; } /** * Get the resource. * * @param url the url in string form. * @return the URL, or null if not found. */ @SuppressWarnings("deprecation") @Override public URL getResource(String url) { String location = getLocationFromUrl(url); if (location == null) { return null; } if (!location.equals(this.location)) { return null; } try { return new URL(null, BYTES_PROTOCOL + "root" + location, new ByteArrayResourceURLStreamHandler(this)); } catch (MalformedURLException e) { throw new IllegalStateException(e); } } /** * Get the resource as a stream. * * @param url the location in URL form * @return the input stream. */ @Override public InputStream getResourceAsStream(String url) { return getResourceAsStreamByLocation(getLocationFromUrl(url)); } /** * Get the resource as stream by location. * * @param location the location. * @return the input stream */ public InputStream getResourceAsStreamByLocation(String location) { if (location == null) { return null; } if (!location.equals(this.location)) { return null; } return new ByteArrayInputStream(bytes); } private String getLocationFromUrl(String url) { if (url == null) { return null; } if (!url.contains(BYTES_PROTOCOL)) { // Already a relative URL, so should be the location return url; } // Relative URL: [bytes://][root][location] eg bytes://root/WEB-INF/web.xml try { @SuppressWarnings("deprecation") URL bytesURL = new URL(url.substring(url.indexOf(BYTES_PROTOCOL))); String hostName = bytesURL.getHost(); if (!"root".equals(hostName)) { return null; } return bytesURL.getPath().replace("//", "/"); } catch (MalformedURLException e) { throw new IllegalStateException(e); } } @Override public String getName() { return location; } @Override public String toString() { return getName() + " " + super.toString(); } } ================================================ FILE: resource/impl/src/main/java/cloud/piranha/resource/impl/ByteArrayResourceStreamHandlerProvider.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; import java.net.spi.URLStreamHandlerProvider; import java.util.function.Function; /** * Handler for the bytes:// protocol. * * @author Arjan Tijms */ public class ByteArrayResourceStreamHandlerProvider extends URLStreamHandlerProvider { /** * A Function that provides the input stream based on the string form of a * bytes:// URL. */ private static InheritableThreadLocal> localGetResourceAsStreamFunction = new InheritableThreadLocal<>(); /** * Default constructor. */ public ByteArrayResourceStreamHandlerProvider() { } @Override public URLStreamHandler createURLStreamHandler(String protocol) { if (!"bytes".equals(protocol)) { return null; } return new URLStreamHandler() { @Override protected URLConnection openConnection(URL u) throws IOException { return new URLConnection(u) { @Override public InputStream getInputStream() throws IOException { return localGetResourceAsStreamFunction.get().apply(u.toString()); } @Override public void connect() throws IOException { // Do nothing } }; } }; } /** * Get the Function set to handle bytes:// protocol handling. * * @return the Function, or null. */ public static Function getGetResourceAsStreamFunction() { return localGetResourceAsStreamFunction.get(); } /** * Sets a Function that provides the input stream based on the string form * of a bytes:// URL. * * @param getResourceAsStreamFunction the function to transform a string URL * to an input stream */ public static void setGetResourceAsStreamFunction( Function getResourceAsStreamFunction) { localGetResourceAsStreamFunction.set(getResourceAsStreamFunction); } } ================================================ FILE: resource/impl/src/main/java/cloud/piranha/resource/impl/ByteArrayResourceURLConnection.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; /** * The URLConnection for a byte-array resource. * * @author Manfred Riem (mriem@manorrock.com) */ class ByteArrayResourceURLConnection extends URLConnection { /** * Stores the byte-array resource. */ private ByteArrayResource resource; /** * Constructor. * * @param url the URL. * @param resource the byte-array resource */ protected ByteArrayResourceURLConnection(URL url, ByteArrayResource resource) { super(url); this.resource = resource; } /** * Connect. * * @throws IOException when an I/O error occurs. */ @Override public void connect() throws IOException { // no-op. } /** * Get the input stream. * * @return the input stream. * @throws IOException when an I/O error occurs. */ @Override public InputStream getInputStream() throws IOException { return new ByteArrayInputStream(resource.getBytes()); } } ================================================ FILE: resource/impl/src/main/java/cloud/piranha/resource/impl/ByteArrayResourceURLStreamHandler.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import java.io.IOException; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; /** * The byte-array resource URL stream handler. * * @author Manfred Riem (mriem@manorrock.com) */ class ByteArrayResourceURLStreamHandler extends URLStreamHandler { /** * Stores the byte-array resource. */ private final ByteArrayResource resource; /** * Constructor. * * @param resource the byte-array resource. */ public ByteArrayResourceURLStreamHandler(ByteArrayResource resource) { this.resource = resource; } /** * Open the connection. * * @param url the URL. * @return the URL connection. * @throws IOException when an I/O error occurs. */ @Override protected URLConnection openConnection(URL url) throws IOException { return new ByteArrayResourceURLConnection(url, resource); } } ================================================ FILE: resource/impl/src/main/java/cloud/piranha/resource/impl/ClassResource.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.stream.Stream; import cloud.piranha.resource.api.Resource; /** * A resource backed by a class. * * @author Manfred Riem (mriem@manorrock.com) */ public class ClassResource implements Resource { /** * Stores the location. */ private String location; /** * Constructor. * * @param className the class name. */ public ClassResource(String className) { try { this.location = "/" + Class.forName(className).getName().replace(".", "/") + ".class"; } catch (ClassNotFoundException ex) { this.location = null; } } @Override public URL getResource(String location) { URL result = null; if (this.location.equals(location)) { return getClass().getResource(location); } return result; } @Override public InputStream getResourceAsStream(String location) { InputStream result = null; if (this.location.equals(location)) { result = getClass().getResourceAsStream(location); } return result; } @Override public Stream getAllLocations() { ArrayList result = new ArrayList<>(); if (location != null) { result.add(location); } return result.stream(); } @Override public String getName() { return location; } @Override public String toString() { return getName() + " " + super.toString(); } } ================================================ FILE: resource/impl/src/main/java/cloud/piranha/resource/impl/DefaultResourceManager.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.stream.Stream; import cloud.piranha.resource.api.Resource; import cloud.piranha.resource.api.ResourceManager; /** * The default ResourceManager. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultResourceManager implements ResourceManager { /** * Stores the resources. */ private final List resources = new ArrayList<>(); /** * Stores the (take a guess) */ private boolean alsoTryLoadFromClass = true; /** * Default constructor. */ public DefaultResourceManager() { } /** * Add resource. * * @param resource the resource. */ @Override public void addResource(Resource resource) { this.resources.add(resource); } /** * Get the resource URL. * * @param location the location. * @return the URL, or null if not found. * @throws MalformedURLException when the location URL is malformed. */ @Override public URL getResource(String location) throws MalformedURLException { URL result = null; for (Resource resource : resources) { result = resource.getResource(location); if (result != null) { break; } } if (alsoTryLoadFromClass && result == null && location != null) { result = getClass().getResource(location); } return result; } /** * Get the resources. * * @param location the location. * @return the collection with the resources. * @throws MalformedURLException when the location URL is malformed. */ @Override public Collection getResources(String location) throws MalformedURLException { ArrayList result = new ArrayList<>(); for (Resource resource : resources) { URL url = resource.getResource(location); if (url != null) { result.add(url); } } if (alsoTryLoadFromClass) { URL url = getClass().getResource(location); if (url != null) { result.add(url); } } return result; } /** * Get the resource as a stream. * * @param location the location. * @return the input stream, or null if not found. */ @Override public InputStream getResourceAsStream(String location) { InputStream result = null; for (Resource resource : resources) { result = resource.getResourceAsStream(location); if (result != null) { break; } } if (alsoTryLoadFromClass && result == null) { result = getClass().getResourceAsStream(location); } return result; } @Override public Stream getAllLocations() { return resources.parallelStream().flatMap(Resource::getAllLocations); } @Override public List getResourceList() { return resources; } @Override public boolean isAlsoTryLoadFromClass() { return alsoTryLoadFromClass; } @Override public void setAlsoTryLoadFromClass(boolean alsoTryLoadFromClass) { this.alsoTryLoadFromClass = alsoTryLoadFromClass; } } ================================================ FILE: resource/impl/src/main/java/cloud/piranha/resource/impl/DefaultResourceManagerClassLoader.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import cloud.piranha.resource.api.ResourceManager; import cloud.piranha.resource.api.ResourceManagerClassLoader; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.System.Logger; import static java.lang.System.Logger.Level.DEBUG; import java.net.MalformedURLException; import java.net.URL; import java.security.CodeSigner; import java.security.CodeSource; import java.security.ProtectionDomain; import static java.util.Collections.enumeration; import static java.util.Collections.list; import java.util.Enumeration; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * The default ResourceManagerClassLoader. * * @author Manfred Riem (mriem@manorrock.com) */ public class DefaultResourceManagerClassLoader extends ClassLoader implements ResourceManagerClassLoader { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(DefaultResourceManagerClassLoader.class.getName()); /** * Stores the 'Unable to load class: ' message prefix. */ private static final String UNABLE_TO_LOAD_CLASS = "Unable to load class: "; /** * Set that keeps a list of classes we know aren't there, so we don't have to search for them again. */ private Set notFoundClasses = ConcurrentHashMap.newKeySet(); /** * Stores the resource manager. */ private ResourceManager resourceManager; /** * Stores the delegate class loader. */ private ClassLoader delegateClassLoader; /** * Stores the loaded classes. */ private final ConcurrentHashMap> classes = new ConcurrentHashMap<>(); /** * Constructor. */ public DefaultResourceManagerClassLoader() { this(getSystemClassLoader()); // Calls the other constructor } /** * Another Constructor. * * @param delegateClassLoader classloader which is consulted first */ public DefaultResourceManagerClassLoader(ClassLoader delegateClassLoader) { this(delegateClassLoader, null); } /** * Yet another Constructor. * * @param resourceManager the resource manager. */ public DefaultResourceManagerClassLoader(ResourceManager resourceManager) { this(null, resourceManager); } /** * Yet another Constructor. * @param classLoader the class loader. * @param resourceManager the resource manager. */ public DefaultResourceManagerClassLoader(ClassLoader classLoader, ResourceManager resourceManager) { super(null); // Calls the super constructor this.delegateClassLoader = classLoader; this.resourceManager = resourceManager; // Assigns the resource manager } /** * Load the class. * * @param name the name. * @param resolve the resolve flag. * @return the class. * @throws ClassNotFoundException when the class cannot be found. */ @Override public Class loadClass(String name, boolean resolve) throws ClassNotFoundException { Class result; try { result = delegateClassLoader.loadClass(name); } catch (ClassNotFoundException cnfe) { result = null; } if (result == null) { try { if (classes.containsKey(name)) { return classes.get(name); } result = internalLoadClass(name, resolve); } catch (Throwable throwable) { throw new ClassNotFoundException(UNABLE_TO_LOAD_CLASS + name, throwable); } } if (result == null) { if (notFoundClasses.contains(name)) { throw new ClassNotFoundException("Unable to load previosly failed to find class: " + name); } notFoundClasses.add(name); ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); if (contextClassLoader != this) { try { result = contextClassLoader.loadClass(name); } catch (ClassNotFoundException e) { // Ignore, throw our own one if needed } if (result != null) { notFoundClasses.remove(name); } } } if (result == null) { // Checks if the result is null throw new ClassNotFoundException(UNABLE_TO_LOAD_CLASS + name); } return result; // Returns the result } @Override protected Class findClass(String moduleName, String name) { try { Class loadedClass = loadClass(name); String loadedModuleName = loadedClass.getModule().getName(); if (loadedModuleName != null && loadedModuleName.equals(moduleName)) { return loadedClass; } } catch (ClassNotFoundException ignored) { } return null; // Returns the null } /** * Inner load class. * * @param name the name. * @param resolve the resolve flog. * @return the class. */ protected Class internalLoadClass(String name, boolean resolve) { Class result = null; try { // Check with the super class. This can contain dynamic classes // that have been "hacked" into our classloader by e.g. Weld or // Javasist. try { result = super.loadClass(name, resolve); } catch (ClassNotFoundException cnfe) { // Ignore } if (result == null) { // Define class byte[] bytes = null; try (InputStream resourceStream = resourceManager.getResourceAsStream(normalizeName(name))) { if (resourceStream == null) { return null; } bytes = readClassBytes(resourceStream); } synchronized (this) { result = classes.get(name); if (result == null) { result = internalDefineClass(name, bytes, resolve); classes.put(name, result); } } } } catch (Throwable throwable) { throw new IllegalStateException(UNABLE_TO_LOAD_CLASS + name, throwable); } return result; } @Override public URL getResource(String name) { URL resource = findResource(name); if (resource == null) { resource = delegateClassLoader.getResource(name); } return resource; } @Override public Enumeration getResources(String name) throws IOException { // Assume for now amount of resources is reasonably small to afford allocating // new collections. List resources = list(delegateClassLoader.getResources(name)); resources.addAll(list(findResources(name))); return enumeration(resources); } @Override protected URL findResource(String name) { URL result = null; try { result = resourceManager.getResource(name); } catch (MalformedURLException mue) { LOGGER.log(DEBUG, "Malformed URL used to find resource", mue); } return result; } @Override protected URL findResource(String moduleName, String name) throws IOException { URL result = null; try { result = resourceManager.getResource(name); } catch (MalformedURLException mue) { LOGGER.log(DEBUG, "Malformed URL used to find resource", mue); } return result; } /** * Find the resources. * * @param name the name of the resource. * @return the enumeration of the resource urls. * @throws IOException when an I/O error occurs. */ @Override protected Enumeration findResources(String name) throws IOException { return enumeration(resourceManager.getResources(name)); } /** * Set the resource manager. * * @param resourceManager the resource manager. */ @Override public void setResourceManager(ResourceManager resourceManager) { this.resourceManager = resourceManager; } @Override public ResourceManager getResourceManager() { return resourceManager; } /** * Set the delegate classloader. * * @param delegateClassLoader the delegate class loader. */ public void setDelegateClassLoader(ClassLoader delegateClassLoader) { this.delegateClassLoader = delegateClassLoader; } /** * {@return the delegate classloader} */ public ClassLoader getDelegateClassLoader() { return delegateClassLoader; } /** * Normalize the name to a .class name. * * @param name the name. * @return the .class name. */ protected String normalizeName(String name) { return name.replace(".", "/") + ".class"; } /** * Read the class bytes from the input stream. * * @param resourceStream the input stream. * @return the bytes. * @throws IOException when an I/O error occurs. */ protected byte[] readClassBytes(InputStream resourceStream) throws IOException { BufferedInputStream inputStream = new BufferedInputStream(resourceStream); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); int read = inputStream.read(); while (read != -1) { outputStream.write((byte) read); read = inputStream.read(); } return outputStream.toByteArray(); } /** * Define the class. * * @param name the name. * @param bytes the bytes. * @param resolve the resolve flag. * @return the class. */ protected Class internalDefineClass(String name, byte[] bytes, boolean resolve) { CodeSource codeSource = new CodeSource(getResource(normalizeName(name)), (CodeSigner[]) null); ProtectionDomain protectionDomain = new ProtectionDomain(codeSource, null); Class result = defineClass(name, bytes, 0, bytes.length, protectionDomain); // Define package String packageName = null; int lastDotPosition = name.lastIndexOf('.'); if (lastDotPosition != -1) { packageName = name.substring(0, lastDotPosition); } if (packageName != null) { Package classPackage = getDefinedPackage(packageName); if (classPackage == null) { try { definePackage(packageName, null, null, null, null, null, null, null); } catch (IllegalArgumentException e) { // Ignore, package already defined } } } if (resolve) { resolveClass(result); } return result; } } ================================================ FILE: resource/impl/src/main/java/cloud/piranha/resource/impl/DirectoryResource.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import cloud.piranha.resource.api.Resource; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import static java.lang.System.Logger.Level.DEBUG; import java.net.MalformedURLException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.function.Predicate; import java.util.stream.Stream; /** * The default DirectoryResource. * * @author Manfred Riem (mriem@manorrock.com) */ public class DirectoryResource implements Resource { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(DirectoryResource.class.getName()); /** * Stores the root directory. */ private File rootDirectory; /** * Constructor. */ public DirectoryResource() { } /** * Constructor. * * @param rootDirectory the root directory. */ public DirectoryResource(String rootDirectory) { this(new File(rootDirectory)); } /** * Constructor. * * @param rootDirectory the root directory. */ public DirectoryResource(File rootDirectory) { this.rootDirectory = rootDirectory; } @Override public Stream getAllLocations() { try { Path rootPath = Paths.get(rootDirectory.toURI()); Path root = Paths.get("/"); return Files.walk(rootPath) .filter(Predicate.not(Files::isDirectory)) .map(rootPath::relativize) .map(root::resolve) .map(p -> p.toString().replace("\\", "/")); } catch (IOException e) { return Stream.empty(); } } @Override public String getName() { return rootDirectory.getName(); } /** * {@return the resource} */ @SuppressWarnings("deprecation") @Override public URL getResource(String location) { URL result = null; if (location != null) { File file = new File(rootDirectory, location); if (file.exists()) { try { result = new URL(file.toURI().toString()); } catch (MalformedURLException mue) { LOGGER.log(DEBUG, "Malformed URL for find directory resource", mue); } } } return result; } /** * @param location the resource location. * @return the input stream, or null if not found. * @see Resource#getResourceAsStream(java.lang.String) */ @Override public InputStream getResourceAsStream(String location) { InputStream result = null; File file = new File(rootDirectory, location); try { result = new FileInputStream(file); } catch (FileNotFoundException fnfe) { LOGGER.log(DEBUG, "Unable to find directory resource", fnfe); } return result; } /** * {@return the root directory} */ public File getRootDirectory() { return this.rootDirectory; } /** * Set the root directory. * * @param rootDirectory the root directory. */ public void setRootDirectory(File rootDirectory) { this.rootDirectory = rootDirectory; } @Override public String toString() { return getName() + " " + super.toString(); } } ================================================ FILE: resource/impl/src/main/java/cloud/piranha/resource/impl/JarResource.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import cloud.piranha.resource.api.Resource; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import static java.lang.System.Logger.Level.WARNING; import java.net.URL; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.stream.Stream; import java.util.zip.ZipEntry; /** * The default JarResource. * * @author Manfred Riem (mriem@manorrock.com) */ public class JarResource implements Resource { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(JarResource.class.getName()); /** * Stores the JAR file. */ private File jarFile; /** * Constructor. */ public JarResource() { } /** * Constructor. * * @param jarFile the JAR file. */ public JarResource(File jarFile) { this.jarFile = jarFile; } /** * {@return the resource} */ @SuppressWarnings("deprecation") @Override public URL getResource(String location) { URL result = null; if (location != null) { try { try ( JarFile jar = new JarFile(jarFile)) { if (jar.getJarEntry(location) != null) { result = new URL("jar:" + jarFile.toURI() + "!/" + location); } } } catch (IOException ioe) { LOGGER.log(WARNING, "I/O error occurred while getting JAR resource", ioe); result = null; } } return result; } /** * Get the resource as a stream. * *

* Note that this method will read the content of a JAR entry into a * byte-array to avoid locking the JAR file. *

* * @param location the resource location. * @return the input stream, or null if not found. * @see Resource#getResourceAsStream(java.lang.String) */ @Override public InputStream getResourceAsStream(String location) { InputStream result = null; try ( JarFile jar = new JarFile(jarFile)) { JarEntry entry = jar.getJarEntry(location.startsWith("/") ? location.substring(1) : location); if (entry != null) { InputStream inputStream; try ( InputStream jarInputStream = jar.getInputStream(entry)) { inputStream = new BufferedInputStream(jarInputStream); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); inputStream.transferTo(outputStream); result = new ByteArrayInputStream(outputStream.toByteArray()); } inputStream.close(); } } catch (IOException ioe) { LOGGER.log(WARNING, "I/O error occurred while getting JAR resource", ioe); } return result; } @Override public Stream getAllLocations() { List entryNames; try ( JarFile jar = new JarFile(jarFile)) { entryNames = jar .stream() .map(ZipEntry::getName) .map(x -> "/" + x) .toList(); } catch (IOException e) { return Stream.of(); } return entryNames.stream(); } /** * {@return the JAR file} */ public File getJarFile() { return this.jarFile; } /** * Set the JAR file. * * @param jarFile the JAR file. */ public void setJarFile(File jarFile) { this.jarFile = jarFile; } @Override public String getName() { return jarFile.getName(); } @Override public String toString() { return getName() + " " + super.toString(); } } ================================================ FILE: resource/impl/src/main/java/cloud/piranha/resource/impl/MultiReleaseResource.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import cloud.piranha.resource.api.Resource; import java.io.IOException; import java.io.InputStream; import static java.lang.System.Logger.Level.WARNING; import java.net.URL; import java.util.Objects; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.util.stream.IntStream; import java.util.stream.Stream; /** * A resource wrapper that loads the versioned entries from META-INF/versions if * the resource contains a main attribute named "Multi-Release" in the * META-INF/MANIFEST.MF * *

* A multi-release resource is a resource that contains a set of "base" entries * and a set of "versioned" entries contained in subdirectories of * "META-INF/versions" directory *

* The versioned entries are partitioned by the major version of the Java * release. A versioned entry, with a version {@code n}, {@code 8 < n}, in the * "META-INF/versions/{n}" directory overrides the base entry as well as any * entry with a version number {@code i} where {@code 8 < i < n} */ public final class MultiReleaseResource implements Resource { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(MultiReleaseResource.class.getName()); /** * Stores the META-INF constant */ private static final String META_INF = "META-INF"; /** * Stores the META-INF/versions/ constant */ private static final String META_INF_VERSIONS = META_INF + "/versions/"; /** * Stores the current version of the runtime */ private static final int CURRENT_VERSION = Runtime.version().feature(); /** * Stores the base release version */ private static final int BASE_RELEASE_VERSION = 8; /** * Stores the resource */ private final Resource resource; /** * Stores if the resource if a multi release */ private final boolean isMultiRelease; /** * Constructor * * @param resource the resource */ public MultiReleaseResource(Resource resource) { this.resource = resource; boolean isMultiReleaseTemp = false; try ( InputStream resourceAsStream = resource.getResourceAsStream("META-INF/MANIFEST.MF")) { if (resourceAsStream != null) { isMultiReleaseTemp = Boolean.parseBoolean(new Manifest(resourceAsStream).getMainAttributes().getValue(Attributes.Name.MULTI_RELEASE)); } } catch (IOException ioe) { LOGGER.log(WARNING, "I/O error occurred while getting manifest for multi release resource", ioe); } isMultiRelease = isMultiReleaseTemp; } @Override public URL getResource(String location) { if (!isMultiRelease) { return resource.getResource(location); } return versionedEntry(location); } /** * Searches in the META-INF/versions for a versioned entry of some resource. * *

* It performs a search in META-INF/versions from the current Java release * until the 9 version (the first version supporting multi-release * resources). * * @param location the location of a resource * @return the URL of the versioned entry if present otherwise the base * entry */ private URL versionedEntry(String location) { if (location.startsWith(META_INF)) { return resource.getResource(location); } return IntStream.iterate(CURRENT_VERSION, version -> version > BASE_RELEASE_VERSION, version -> --version) .mapToObj(version -> resource.getResource(META_INF_VERSIONS + version + "/" + location)) .filter(Objects::nonNull) .findFirst() .orElseGet(() -> resource.getResource(location)); } @Override public InputStream getResourceAsStream(String location) { if (!isMultiRelease) { return resource.getResourceAsStream(location); } try { URL url = versionedEntry(location); if (url != null) { return url.openStream(); } } catch (IOException ioe) { LOGGER.log(WARNING, "I/O error occurred while getting multi release resource", ioe); } return null; } @Override public Stream getAllLocations() { return resource.getAllLocations(); } @Override public String getName() { return resource.getName(); } @Override public String toString() { return getName() + " " + super.toString(); } } ================================================ FILE: resource/impl/src/main/java/cloud/piranha/resource/impl/PrefixJarResource.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import cloud.piranha.resource.api.Resource; import java.io.IOException; import java.io.InputStream; import java.lang.System.Logger; import static java.lang.System.Logger.Level.WARNING; import java.net.MalformedURLException; import java.net.URL; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.stream.Stream; /** * The default PrefixJarResource. * * @author Manfred Riem (mriem@manorrock.com) */ public class PrefixJarResource implements Resource { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(PrefixJarResource.class.getName()); /** * Stores the JAR file. */ private JarFile jarFile; /** * Stores the prefix. */ private String prefix; /** * Constructor. */ public PrefixJarResource() { this.jarFile = null; this.prefix = null; } /** * Constructor. * * @param jarFile the JAR file. * @param prefix the prefix. */ public PrefixJarResource(JarFile jarFile, String prefix) { this.jarFile = jarFile; if (!prefix.endsWith("/")) { this.prefix = prefix + "/"; } else { this.prefix = prefix; } } @SuppressWarnings("deprecation") @Override public URL getResource(String location) { URL result = null; try { location = prefix + location; JarEntry jarEntry = jarFile.getJarEntry(location); if (jarEntry != null) { result = new URL("jar://" + jarFile.getName() + "#!/" + location); } } catch (MalformedURLException mue) { LOGGER.log(WARNING, "Malformed URL passed for prefix JAR resource", mue); } return result; } @Override public InputStream getResourceAsStream(String location) { InputStream result = null; location = prefix + location; JarEntry jarEntry = jarFile.getJarEntry(location); if (jarEntry != null) { try { result = jarFile.getInputStream(jarEntry); } catch (IOException ioe) { LOGGER.log(WARNING, "I/O error occurred while getting prefix JAR resource", ioe); } } return result; } @Override public Stream getAllLocations() { return Stream.empty(); } /** * {@return the JAR file} */ public JarFile getJarFile() { return this.jarFile; } /** * {@return the prefix} */ public String getPrefix() { return this.prefix; } /** * Set the JAR file. * * @param jarFile the JAR file. */ public void setJarFile(JarFile jarFile) { this.jarFile = jarFile; } /** * Set the prefix. * * @param prefix the prefix. */ public void setPrefix(String prefix) { this.prefix = prefix; if (!prefix.endsWith("/")) { this.prefix = prefix + "/"; } } @Override public String getName() { return jarFile.getName(); } @Override public String toString() { return getName() + " " + super.toString(); } } ================================================ FILE: resource/impl/src/main/java/cloud/piranha/resource/impl/StringResource.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; /** * A resource based on a string value. * * @author Arjan Tijms */ public class StringResource extends ByteArrayResource { /** * Creates the resource using a string value * @param location the location of the value * @param value the value itself */ public StringResource(String location, String value) { super(location, value.getBytes()); } } ================================================ FILE: resource/impl/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ import java.net.spi.URLStreamHandlerProvider; import cloud.piranha.resource.impl.ByteArrayResourceStreamHandlerProvider; /** * This module delivers the default implementation of the Resource API. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.resource.impl { exports cloud.piranha.resource.impl; opens cloud.piranha.resource.impl; provides URLStreamHandlerProvider with ByteArrayResourceStreamHandlerProvider; requires transitive cloud.piranha.resource.api; } ================================================ FILE: resource/impl/src/main/resources/META-INF/services/java.net.spi.URLStreamHandlerProvider ================================================ cloud.piranha.resource.impl.ByteArrayResourceStreamHandlerProvider ================================================ FILE: resource/impl/src/test/java/cloud/piranha/resource/impl/AliasedDirectoryResourceTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import java.io.File; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; /** * The JUnit test for AliasedDirectoryResource. * * @author Manfred Riem (mriem@manorrock.com) */ class AliasedDirectoryResourceTest { /** * Test getResource. */ @Test void testGetResource() { AliasedDirectoryResource resource = new AliasedDirectoryResource(null, "/alias"); assertNull(resource.getResource("resource")); } /** * Test getResource. */ @Test void testGetResource2() { AliasedDirectoryResource resource = new AliasedDirectoryResource(); resource.setRootDirectory(new File("src/main/java")); resource.setAlias("/alias"); assertNotNull(resource.getResource("/alias/cloud/piranha/resource/impl/DirectoryResource.java")); } /** * Test getResource method. */ @Test void testGetResource3() { AliasedDirectoryResource resource = new AliasedDirectoryResource(new File("src/test/java/org"), "/org"); assertNull(resource.getResource("/cloud/piranha/resource/impl/tests/AliasedDirectoryResourceTest2.java")); } /** * Test getResourceAsStream method. */ @Test void testGetResourceAsStream() { AliasedDirectoryResource resource = new AliasedDirectoryResource(null, "/alias"); assertNull(resource.getResourceAsStream("/resource")); } /** * Test getResourceAsStream method. */ @Test void testGetResourceAsStream2() { AliasedDirectoryResource resource = new AliasedDirectoryResource(new File("src/main/java"), "/alias"); assertNotNull(resource.getResourceAsStream("/alias/cloud/piranha/resource/impl/DirectoryResource.java")); } /** * Test getResourceAsStream method. */ @Test void testGetResourceAsStream3() { AliasedDirectoryResource resource = new AliasedDirectoryResource(new File("src/main/java"), "/alias"); assertNull(resource.getResourceAsStream("/alias/cloud/piranha/impl/DirectoryResource.class")); } /** * Test getAlias method. */ @Test void testGetAlias() { AliasedDirectoryResource resource = new AliasedDirectoryResource(null, "/alias"); assertEquals("/alias", resource.getAlias()); } /** * Test getRootDirectory method. */ @Test void testGetRootDirectory() { AliasedDirectoryResource resource = new AliasedDirectoryResource(); resource.setRootDirectory(new File("src/main/java")); resource.setAlias("/alias"); assertNotNull(resource.getRootDirectory()); } } ================================================ FILE: resource/impl/src/test/java/cloud/piranha/resource/impl/AliasedNamedResourceTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import cloud.piranha.resource.api.Resource; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class AliasedNamedResourceTest { @Test void testGetName() { Resource resource = new AliasedNamedResource(new ByteArrayResource("/", new byte[0]), "custom name"); assertEquals("custom name", resource.getName()); } } ================================================ FILE: resource/impl/src/test/java/cloud/piranha/resource/impl/ByteArrayResourceTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * The JUnit tests for the ByteArrayResource class. * * @author Manfred Riem (mriem@manorrock.com) */ class ByteArrayResourceTest { /** * Test getAllLocations method. */ @Test void testGetAllLocations() { ByteArrayResource resource = new ByteArrayResource("mylocation", null); assertEquals(1, resource.getAllLocations().count()); } /** * Test getBytes method. */ @Test void testGetBytes() { ByteArrayResource resource = new ByteArrayResource("", null); assertNull(resource.getBytes()); } /** * Test getResource method. * * @throws Exception when a serious error occurs. */ @Test void testGetResource() throws Exception { ByteArrayResource resource = new ByteArrayResource("mylocation", new byte[0]); assertNotNull(resource.getResource("mylocation")); assertNull(resource.getResource("notmylocation")); assertTrue(resource.getResource("mylocation").openStream() instanceof ByteArrayInputStream); } /** * Test getResourceAsStream method. */ @Test void testGetResourceAsStream() { ByteArrayResource resource = new ByteArrayResource("mylocation", new byte[0]); assertTrue(resource.getResourceAsStream("mylocation") instanceof ByteArrayInputStream); assertNull(resource.getResourceAsStream("notmylocation")); } } ================================================ FILE: resource/impl/src/test/java/cloud/piranha/resource/impl/DefaultResourceManagerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import java.io.File; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import org.junit.jupiter.api.Test; /** * The JUnit tests for the DefaultResourceManager class. * * @author Manfred Riem (mriem@manorrock.com) */ class DefaultResourceManagerTest { /** * Test getResource method to not find a missing resource. * * @throws Exception when a serious error occurs. */ @Test void testGetResource() throws Exception { DefaultResourceManager manager = new DefaultResourceManager(); manager.addResource(new DirectoryResource(new File(""))); assertNull(manager.getResource("/doesnotexist")); } /** * Test getResource method to find the /src directory resource. * * @throws Exception when a serious error occurs. */ @Test void testGetResource2() throws Exception { DefaultResourceManager manager = new DefaultResourceManager(); manager.addResource(new DirectoryResource(new File("."))); assertNotNull(manager.getResource("src")); } /** * Test getResourceAsStream method. * * @throws Exception when a serious error occurs. */ @Test void testGetResource3() throws Exception { DefaultResourceManager manager = new DefaultResourceManager(); manager.addResource(new DirectoryResource(new File("."))); manager.addResource(new DirectoryResource(new File(""))); assertNotNull(manager.getResource("/src/main/java/cloud/piranha/resource/impl/DefaultResourceManager.java")); } /** * Test getResourceAsStream method to not find a missing resource. */ @Test void testGetResourceAsStream() { DefaultResourceManager manager = new DefaultResourceManager(); manager.addResource(new DirectoryResource(new File(""))); assertNull(manager.getResourceAsStream("/doesnotexist")); } /** * Test getResourceAsStream method to not find a missing resource. */ @Test void testGetResourceAsStream2() { DefaultResourceManager manager = new DefaultResourceManager(); manager.addResource(new DirectoryResource(new File("."))); manager.addResource(new DirectoryResource(new File(""))); assertNotNull(manager.getResourceAsStream("/src/main/java/cloud/piranha/resource/impl/DefaultResourceManager.java")); } } ================================================ FILE: resource/impl/src/test/java/cloud/piranha/resource/impl/DirectoryResourceTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import org.junit.jupiter.api.Test; import java.io.File; import static org.junit.jupiter.api.Assertions.*; /** * The JUnit tests for DirectoryResource class. * * @author Manfred Riem (mriem@manorrock.com) */ class DirectoryResourceTest { /** * Test getResource method. */ @Test void testGetResource() { DirectoryResource resource = new DirectoryResource(); assertNull(resource.getResource("/resource")); } /** * Test getResourceAsStream method. */ @Test void testGetResourceAsStream() { DirectoryResource resource = new DirectoryResource(); assertNull(resource.getResourceAsStream("/resource")); } /** * Test getResourceAsStream method. */ @Test void testGetResourceAsStream2() { DirectoryResource resource = new DirectoryResource(new File(".")); assertNotNull(resource.getResourceAsStream("pom.xml")); } /** * Test getRootDirectory method, of class DirectoryResource. */ @Test void testGetRootDirectory() { DirectoryResource resource = new DirectoryResource(); resource.setRootDirectory(new File("src/main/java")); assertNotNull(resource.getRootDirectory()); } } ================================================ FILE: resource/impl/src/test/java/cloud/piranha/resource/impl/JarResourceTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import org.junit.jupiter.api.Test; import java.io.File; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; /** * The JUnit tests for the DefaultJarResource class. * * @author Manfred Riem (mriem@manorrock.com) */ class JarResourceTest { /** * Test getResource method. */ @Test void testGetResource() { JarResource resource = new JarResource(); assertNull(resource.getResource(null)); } /** * Test getResource method. */ @Test void testGetResource2() { JarResource resource = new JarResource(); assertThrows(NullPointerException.class, () -> resource.getResource("we_wont_find_this")); } /** * Test getResource method. */ @Test void testGetResource3() { JarResource resource = new JarResource(); resource.setJarFile(new File("this_jar_file_does_not_exist.jar")); assertNull(resource.getResource("we_wont_find_this")); } } ================================================ FILE: resource/impl/src/test/java/cloud/piranha/resource/impl/MultiReleaseResourceTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import cloud.piranha.resource.api.Resource; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.stream.Stream; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; class MultiReleaseResourceTest { private static final Resource MANIFEST_MULTI_RELEASE = new ByteArrayResource("META-INF/MANIFEST.MF", """ Manifest-Version: 1.0 Multi-Release: true """.getBytes(StandardCharsets.UTF_8)); private static final byte[] FOO_BYTES = "FOO".getBytes(StandardCharsets.UTF_8); private static final byte[] BAR_BYTES = "BAR".getBytes(StandardCharsets.UTF_8); private static final byte[] BAZ_BYTES = "BAZ".getBytes(StandardCharsets.UTF_8); private static Resource compose(Resource... resources) { return new Resource() { @Override public URL getResource(String location) { for (Resource resource : resources) { URL resourceURL = resource.getResource(location); if (resourceURL != null) return resourceURL; } return null; } @Override public InputStream getResourceAsStream(String location) { for (Resource resource : resources) { InputStream resourceAsStream = resource.getResourceAsStream(location); if (resourceAsStream != null) return resourceAsStream; } return null; } @Override public Stream getAllLocations() { return Arrays.stream(resources).flatMap(Resource::getAllLocations); } }; } @Test void testNormalResource() throws IOException { Resource resource = new MultiReleaseResource(new ByteArrayResource("foo", FOO_BYTES)); assertNotNull(resource); assertArrayEquals(FOO_BYTES, resource.getResource("foo").openStream().readAllBytes()); } @Test void testResourceWithoutMultiReleaseManifest() throws IOException { Resource resource = new MultiReleaseResource( compose(new ByteArrayResource("foo", FOO_BYTES), new ByteArrayResource("META-INF/versions/11/foo", BAR_BYTES), new ByteArrayResource("META-INF/MANIFEST.MF", "Manifest-Version: 1.0".getBytes(StandardCharsets.UTF_8))) ); assertArrayEquals(FOO_BYTES, resource.getResource("foo").openStream().readAllBytes()); } @Test void testResourceWithMultiReleaseManifest() throws IOException { Resource resource = new MultiReleaseResource( compose(new ByteArrayResource("foo", FOO_BYTES), new ByteArrayResource("META-INF/versions/9/foo", BAR_BYTES), MANIFEST_MULTI_RELEASE)); assertArrayEquals(BAR_BYTES, resource.getResource("foo").openStream().readAllBytes()); } @Test void testResourceWithMultiReleaseManifest2() throws IOException { Resource resource = new MultiReleaseResource( compose(new ByteArrayResource("foo", FOO_BYTES), new ByteArrayResource("META-INF/versions/8/foo", BAR_BYTES), MANIFEST_MULTI_RELEASE)); assertArrayEquals(FOO_BYTES, resource.getResource("foo").openStream().readAllBytes()); } @Test void testResourceWithMultiVersionsManifest() throws IOException { Resource resource = new MultiReleaseResource( compose(new ByteArrayResource("foo", FOO_BYTES), new ByteArrayResource("META-INF/versions/11/foo", BAR_BYTES), new ByteArrayResource("META-INF/versions/%d/foo".formatted(Runtime.version().feature()), BAZ_BYTES), MANIFEST_MULTI_RELEASE)); assertArrayEquals(BAZ_BYTES, resource.getResource("foo").openStream().readAllBytes()); } @Test void testResourceNotMultiRelease() throws IOException { Resource resource = new MultiReleaseResource( compose(new ByteArrayResource("foo", FOO_BYTES), new ByteArrayResource("META-INF/versions/11/foo", BAR_BYTES), new ByteArrayResource("baz", BAZ_BYTES), MANIFEST_MULTI_RELEASE)); assertArrayEquals(BAZ_BYTES, resource.getResource("baz").openStream().readAllBytes()); } @Test void testMetaInfResource() throws IOException { Resource resource = new MultiReleaseResource( compose(new ByteArrayResource("META-INF/foo", FOO_BYTES), MANIFEST_MULTI_RELEASE)); assertArrayEquals(FOO_BYTES, resource.getResource("META-INF/foo").openStream().readAllBytes()); } @Test void testMetaInfResource2() throws IOException { Resource resource = new MultiReleaseResource( compose(new ByteArrayResource("META-INF/foo", FOO_BYTES), new ByteArrayResource("META-INF/versions/11/META-INF/foo", BAR_BYTES), MANIFEST_MULTI_RELEASE)); assertArrayEquals(FOO_BYTES, resource.getResource("META-INF/foo").openStream().readAllBytes()); } } ================================================ FILE: resource/impl/src/test/java/cloud/piranha/resource/impl/PrefixJarResourceTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.impl; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; /** * The JUnit tests for the DefaultJarResource class. * * @author Manfred Riem (mriem@manorrock.com) */ class PrefixJarResourceTest { /** * Test getResource method. */ @Test void testGetResource() { PrefixJarResource resource = new PrefixJarResource(); assertThrows(NullPointerException.class, () -> assertNull(resource.getResource(null))); } /** * Test getResource method. */ @Test void testGetResource2() { PrefixJarResource resource = new PrefixJarResource(); assertThrows(NullPointerException.class, () -> resource.getResource("we_wont_find_this")); } } ================================================ FILE: resource/pom.xml ================================================ 4.0.0 cloud.piranha project 25.4.0-SNAPSHOT cloud.piranha.resource project pom Piranha - Resource api impl shrinkwrap cloud.piranha bom ${project.version} pom import org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test default file:///tmp/piranha/resource/ ================================================ FILE: resource/shrinkwrap/pom.xml ================================================ 4.0.0 cloud.piranha.resource project 25.4.0-SNAPSHOT piranha-resource-shrinkwrap jar Piranha - Resource - Shrinkwrap Integration org.apache.commons commons-compress compile org.jboss.shrinkwrap shrinkwrap-impl-base compile cloud.piranha.core piranha-core-impl ${project.version} provided org.apache.maven.plugins maven-surefire-plugin --patch-module shrinkwrap.api=${settings.localRepository}/org/jboss/shrinkwrap/shrinkwrap-impl-base/2.0.0-beta-2/shrinkwrap-impl-base-2.0.0-beta-2.jar ================================================ FILE: resource/shrinkwrap/src/main/java/cloud/piranha/resource/shrinkwrap/ArchiveURLStreamHandler.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.shrinkwrap; import static java.lang.System.Logger.Level.WARNING; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLStreamHandler; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.api.ArchivePaths; import org.jboss.shrinkwrap.api.Node; import org.jboss.shrinkwrap.api.asset.Asset; /** * A Shrinkwrap Archive URL stream handler. * * @author Arjan Tijms */ public class ArchiveURLStreamHandler extends URLStreamHandler { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(ArchiveURLStreamHandler.class.getName()); /** * Stores the archive. */ private Archive archive; /** * Constructor. * * @param archive the archive. */ public ArchiveURLStreamHandler(Archive archive) { this.archive = archive; } @Override protected StreamConnection openConnection(URL requestedUrl) throws IOException { return new StreamConnection(requestedUrl) { String contentType; @Override public InputStream getInputStream() throws IOException { Node node = getNode(); if (node == null) { throw new IOException("Can't resolve URL " + requestedUrl.toExternalForm()); } Asset asset = node.getAsset(); if (asset == null) { return null; } return asset.openStream(); } @Override public String getContentType() { if (contentType != null) { return contentType; } try { InputStream stream = getInputStream(); if (stream != null) { contentType = guessContentTypeFromStream(stream); } } catch (IOException ioe) { LOGGER.log(WARNING, "An I/O error occurred while guessing content type from stream", ioe); } if (contentType == null) { Node node = getNode(); if (node != null) { contentType = guessContentTypeFromName(node.getPath().get()); } } if (contentType == null) { contentType = "content/unknown"; } return contentType; } /** * {@return the node} */ private Node getNode() { return archive.get( ArchivePaths.create( requestedUrl .getPath() .replace(archive.getName(), ""))); } }; } } ================================================ FILE: resource/shrinkwrap/src/main/java/cloud/piranha/resource/shrinkwrap/GlobalArchiveStreamHandler.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.shrinkwrap; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; import cloud.piranha.core.api.WebApplication; /** * Stream handler for the shrinkwrap protocol (urls starting with * shrinkwrap://). * *

* This is for URLs that don't have the embedded stream handler, which is for * instance the case when resource URLs obtained from Piranha Micro are * converted to external string form and used to create a new URL. * * @author Arjan Tijms * */ public class GlobalArchiveStreamHandler extends URLStreamHandler { /** * Stores the web application. */ private WebApplication webApplication; /** * Constructror. * * @param webApplication the web application. */ public GlobalArchiveStreamHandler(WebApplication webApplication) { this.webApplication = webApplication; } /** * Connect to the URL. * * @param requestedUrl the requested URL. * @return the URL connection. */ public URLConnection connect(URL requestedUrl) { try { return openConnection(requestedUrl); } catch (IOException e) { throw new IllegalStateException(e); } } @Override public URLConnection openConnection(URL requestedUrl) throws IOException { return new StreamConnection(requestedUrl) { @Override public InputStream getInputStream() throws IOException { InputStream inputStream = webApplication.getResourceAsStream(requestedUrl.toString()); if (inputStream == null) { inputStream= Thread.currentThread().getContextClassLoader().getResourceAsStream(requestedUrl.toString()); } return inputStream; } }; } } ================================================ FILE: resource/shrinkwrap/src/main/java/cloud/piranha/resource/shrinkwrap/IsolatingResourceManagerClassLoader.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.shrinkwrap; import java.io.IOException; import java.net.URL; import java.util.Enumeration; import cloud.piranha.resource.api.ResourceManager; import cloud.piranha.resource.impl.DefaultResourceManagerClassLoader; /** * The default WebApplicationClassLoader. * * @author Arjan Tijms */ public class IsolatingResourceManagerClassLoader extends DefaultResourceManagerClassLoader { /** * Stores the Shrinkwrap package prefix. */ private static final String SHRINKWRAP_PACKAGE_PREFIX = "org.jboss.shrinkwrap"; /** * Stores the system classloader. */ private final ClassLoader systemClassLoader; /** * Stores the classloader id. */ private final String classLoaderId; /** * Constructor. */ public IsolatingResourceManagerClassLoader() { this(""); } /** * Constructor. * * @param classLoaderId the id for the class loader. */ public IsolatingResourceManagerClassLoader(String classLoaderId) { this(getSystemClassLoader().getParent(), classLoaderId); } /** * Constructor. * * @param classLoader the delegate class loader. * @param classLoaderId the id for the class loader. */ public IsolatingResourceManagerClassLoader(ClassLoader classLoader, String classLoaderId) { super(classLoader); this.systemClassLoader = getSystemClassLoader(); this.classLoaderId = classLoaderId; } @Override public Class loadClass(String name, boolean resolve) throws ClassNotFoundException { if (name.startsWith(SHRINKWRAP_PACKAGE_PREFIX)) { return systemClassLoader.loadClass(name); } return super.loadClass(name, resolve); } @Override public URL getResource(String name) { if (name.startsWith(SHRINKWRAP_PACKAGE_PREFIX)) { return systemClassLoader.getResource(name); } return super.getResource(name); } @Override public Enumeration getResources(String name) throws IOException { if (name.startsWith(SHRINKWRAP_PACKAGE_PREFIX)) { return systemClassLoader.getResources(name); } return super.getResources(name); } /** * {@return the classloader id} */ public String getClassLoaderId() { return classLoaderId; } @Override public void setResourceManager(ResourceManager resourceManager) { resourceManager.setAlsoTryLoadFromClass(false); super.setResourceManager(resourceManager); } } ================================================ FILE: resource/shrinkwrap/src/main/java/cloud/piranha/resource/shrinkwrap/NodeURLStreamHandler.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.shrinkwrap; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; import java.util.Collection; import java.util.Optional; import org.jboss.shrinkwrap.api.Node; import org.jboss.shrinkwrap.api.asset.Asset; /** * Stream handler used for URLs that represent directories. * * @author Arjan Tijms */ public class NodeURLStreamHandler extends URLStreamHandler { /** * Stores the nodes. */ private Collection nodes; /** * Constructor. * * @param nodes the collection of nodes. */ public NodeURLStreamHandler(Collection nodes) { this.nodes = nodes; } @Override protected URLConnection openConnection(URL requestedUrl) throws IOException { return new StreamConnection(requestedUrl) { @Override public InputStream getInputStream() throws IOException { if (requestedUrl == null) { return null; } // Relative URL: [shrinkwrap://][jar name][location] String location = requestedUrl.getPath(); if ("/".equals(location) || "".equals(location)) { return new ShrinkWrapDirectoryInputStream(nodes); } Optional optionalNode = nodes.stream() .filter(node -> node.getPath().get().equals(location)) .findAny(); if (!optionalNode.isPresent()) { return null; } Asset asset = optionalNode.get().getAsset(); if (asset != null) { return asset.openStream(); } return new ShrinkWrapDirectoryInputStream(getDirectoryContent(location)); } }; } private Collection getDirectoryContent(String location) { return nodes.stream() .filter(node -> node.getPath().get().startsWith(location)) .toList(); } } ================================================ FILE: resource/shrinkwrap/src/main/java/cloud/piranha/resource/shrinkwrap/ShrinkWrapDirectoryInputStream.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.shrinkwrap; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.Collection; import java.util.Iterator; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import org.jboss.shrinkwrap.api.Node; /** * Input stream for reading from an archive entry that represents a directory. * * @author Arjan Tijms */ public class ShrinkWrapDirectoryInputStream extends ZipInputStream { /** * Stores the child node iterator. */ private Iterator childrenIterator; /** * Constructor. * * @param nodes the nodes. */ public ShrinkWrapDirectoryInputStream(Collection nodes) { super(new ByteArrayInputStream(new byte[]{})); childrenIterator = nodes.iterator(); } @Override public ZipEntry getNextEntry() throws IOException { if (!childrenIterator.hasNext()) { return null; } return new ZipEntry(childrenIterator.next().getPath().get()); } } ================================================ FILE: resource/shrinkwrap/src/main/java/cloud/piranha/resource/shrinkwrap/ShrinkWrapResource.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.shrinkwrap; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLStreamHandler; import java.util.Collection; import java.util.stream.Stream; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.api.ArchivePath; import org.jboss.shrinkwrap.api.ArchivePaths; import org.jboss.shrinkwrap.api.Node; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.Asset; import org.jboss.shrinkwrap.api.spec.JavaArchive; import cloud.piranha.resource.api.Resource; /** * A ShrinkWrap resource. * * @author Arjan Tijms */ public class ShrinkWrapResource implements Resource { /** * Stores the 'shrinkwrap://' protocol constant. */ private static final String SHRINKWRAP_PROTOCOL = "shrinkwrap://"; /** * Stores the archive. */ private Archive archive; /** * Stores the archive stream handler. */ private ArchiveURLStreamHandler archiveStreamHandler; /** * Constructor. * * @param resourcesPath the resources path. * @param archive the archive. */ public ShrinkWrapResource(String resourcesPath, Archive archive) { this(resourcesPath, archive, null); } /** * Constructor. * * @param resourcesPath the resources path. * @param archive the archive. * @param resourceName the resource name */ public ShrinkWrapResource(String resourcesPath, Archive archive, String resourceName) { JavaArchive newArchive = resourceName != null ? ShrinkWrap.create(JavaArchive.class, resourceName) : ShrinkWrap.create(JavaArchive.class); getAllLocations(archive) .filter(path -> path.startsWith(resourcesPath)) .forEach(path -> newArchive.add( archive.get(path).getAsset(), path.substring(resourcesPath.length()))); this.archive = newArchive; archiveStreamHandler = new ArchiveURLStreamHandler(this.archive); } /** * Constructor. * * @param archive the archive. */ public ShrinkWrapResource(Archive archive) { this.archive = archive.shallowCopy(); archiveStreamHandler = new ArchiveURLStreamHandler(this.archive); } @SuppressWarnings("deprecation") @Override public URL getResource(String url) { String location = getLocationFromUrl(url); if (location == null) { return null; } Node node = getNode(archive, location); if (node == null) { return null; } URLStreamHandler streamHandler = archiveStreamHandler; Asset asset = node.getAsset(); if (asset == null) { // Node was a directory streamHandler = new NodeURLStreamHandler(getContentFromNode(node)); } try { String slashFix; if (location.startsWith("/")) { slashFix = ""; } else { slashFix = archive.getName().endsWith("/") ? "" : "/"; } return new URL(null, SHRINKWRAP_PROTOCOL + archive.getName() + slashFix + location, streamHandler); } catch (MalformedURLException e) { throw new IllegalStateException(e); } } @Override public InputStream getResourceAsStream(String url) { return getResourceAsStreamByLocation(getLocationFromUrl(url)); } /** * Get the resource as stream by location. * * @param location the location. * @return the input stream */ public InputStream getResourceAsStreamByLocation(String location) { if (location == null) { return null; } Node node = getNode(archive, location); if (node == null) { return null; } Asset asset = node.getAsset(); if (asset == null) { // Node was a directory return new ShrinkWrapDirectoryInputStream(getContentFromNode(node)); } // Node was an asset return asset.openStream(); } @Override public Stream getAllLocations() { return getAllLocations(archive); } /** * Get all locations. * * @param archiveToGetFrom the archive to use. * @return the stream of locations. */ public Stream getAllLocations(Archive archiveToGetFrom) { return archiveToGetFrom.getContent() .keySet() .stream() .map(ArchivePath::get) .filter(e -> getAsset(archiveToGetFrom, e) != null); } /** * Get the embedded archive. * * @return the embedded archive that contains the actual data */ public Archive getArchive() { return archive; } private Collection getContentFromNode(Node node) { return archive.getContent( e -> e.get().startsWith(node.getPath().get())) .values(); } private String getLocationFromUrl(String url) { if (url == null) { return null; } if (!url.contains(SHRINKWRAP_PROTOCOL)) { // Already a relative URL, so should be the location return url; } // Relative URL: [shrinkwrap://][jar name][location] try { @SuppressWarnings("deprecation") URL archiveURL = new URL(url.substring(url.indexOf(SHRINKWRAP_PROTOCOL))); String archiveName = archiveURL.getHost(); if (!archive.getName().equals(archiveName)) { return null; } return archiveURL.getPath().replace("//", "/"); } catch (MalformedURLException e) { throw new IllegalStateException(e); } } private Asset getAsset(Archive archiveToGetFrom, String location) { Node node = getNode(archiveToGetFrom, location); if (node == null) { return null; } Asset asset = node.getAsset(); if (asset == null) { return null; } return asset; } private Node getNode(Archive archiveToGetFrom, String location) { return archiveToGetFrom.get( ArchivePaths.create( location)); } @Override public String getName() { return archive.getName(); } } ================================================ FILE: resource/shrinkwrap/src/main/java/cloud/piranha/resource/shrinkwrap/StreamConnection.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.shrinkwrap; import java.io.IOException; import java.net.URL; import java.net.URLConnection; /** * A stream connection. * * @author Arjan Tijms */ public abstract class StreamConnection extends URLConnection { /** * Constructor. * * @param url the URL. */ protected StreamConnection(URL url) { super(url); } @Override public void connect() throws IOException { // Do nothing } } ================================================ FILE: resource/shrinkwrap/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Shrinkwrap implementation of the Resource API. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.resource.shrinkwrap { exports cloud.piranha.resource.shrinkwrap; opens cloud.piranha.resource.shrinkwrap; requires static transitive cloud.piranha.core.api; requires static cloud.piranha.core.impl; requires cloud.piranha.resource.impl; requires transitive shrinkwrap.api; } ================================================ FILE: resource/shrinkwrap/src/test/java/cloud/piranha/resource/shrinkwrap/DefaultResourceManagerTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.shrinkwrap; import static org.jboss.shrinkwrap.api.ShrinkWrap.create; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.InputStream; import java.net.URL; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.jupiter.api.Test; import cloud.piranha.resource.api.Resource; import cloud.piranha.resource.impl.DefaultResourceManager; /** * The JUnit tests for ShrinkWrapResource * * @author Arjan Tijms */ class DefaultResourceManagerTest { /** * Test getResource method. * * @throws Exception when a serious error occurs. */ @Test void testGetResource() throws Exception { Resource resource = new ShrinkWrapResource(create(WebArchive.class) .addClass(DefaultResourceManagerTest.class)); DefaultResourceManager manager = new DefaultResourceManager(); manager.addResource(resource); URL resourceUrl = manager.getResource("/WEB-INF/classes/cloud/piranha/resource/shrinkwrap/DefaultResourceManagerTest.class"); assertNotNull(resourceUrl); byte[] buffer = new byte[4096]; int read = resourceUrl.openStream().read(buffer); assertTrue(read > 0); } @Test void testGetResourceAsStream() throws Exception { Resource resource = new ShrinkWrapResource(create(WebArchive.class) .addClass(DefaultResourceManagerTest.class)); DefaultResourceManager manager = new DefaultResourceManager(); manager.addResource(resource); InputStream resourceStream = manager.getResourceAsStream("/WEB-INF/classes/cloud/piranha/resource/shrinkwrap/DefaultResourceManagerTest.class"); assertNotNull(resourceStream); byte[] buffer = new byte[4096]; int read = resourceStream.read(buffer); assertTrue(read > 0); } @Test void testGetResourceFlattenPath() throws Exception { Resource resource = new ShrinkWrapResource("/WEB-INF/classes", create(WebArchive.class) .addClass(DefaultResourceManagerTest.class)); DefaultResourceManager manager = new DefaultResourceManager(); manager.addResource(resource); URL resourceUrl = manager.getResource("/cloud/piranha/resource/shrinkwrap/DefaultResourceManagerTest.class"); assertNotNull(resourceUrl); byte[] buffer = new byte[4096]; int read = resourceUrl.openStream().read(buffer); assertTrue(read > 0); } @Test void testGetResourceAsStreamFlattenPath() throws Exception { Resource resource = new ShrinkWrapResource("/WEB-INF/classes", create(WebArchive.class) .addClass(DefaultResourceManagerTest.class)); DefaultResourceManager manager = new DefaultResourceManager(); manager.addResource(resource); InputStream resourceStream = manager.getResourceAsStream("/cloud/piranha/resource/shrinkwrap/DefaultResourceManagerTest.class"); assertNotNull(resourceStream); byte[] buffer = new byte[4096]; int read = resourceStream.read(buffer); assertTrue(read > 0); } } ================================================ FILE: resource/shrinkwrap/src/test/java/cloud/piranha/resource/shrinkwrap/DefaultWebApplicationClassLoaderTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.resource.shrinkwrap; import static org.jboss.shrinkwrap.api.ShrinkWrap.create; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.jupiter.api.Test; import cloud.piranha.resource.api.Resource; import cloud.piranha.resource.impl.DefaultResourceManager; /** * The JUnit tests for the IsolatingResourceManagerClassLoader. * * @author Arjan Tijms */ class DefaultWebApplicationClassLoaderTest { /** * Test loadClass method. * * @throws Exception when a serious error occurs. */ @Test void testLoadClass() throws Exception { IsolatingResourceManagerClassLoader classLoader = new IsolatingResourceManagerClassLoader("test"); Resource resource = new ShrinkWrapResource("/WEB-INF/classes", create(WebArchive.class) .addClass(DefaultResourceManagerTest.class)); DefaultResourceManager manager = new DefaultResourceManager(); manager.addResource(resource); classLoader.setResourceManager(manager); Class clazz = classLoader.loadClass(DefaultResourceManagerTest.class.getName()); assertNotNull(clazz); //assertEquals(clazz.getClassLoader(), classLoader); } } ================================================ FILE: single/pom.xml ================================================ 4.0.0 cloud.piranha project 25.4.0-SNAPSHOT piranha-single jar Piranha - Single cloud.piranha bom ${project.version} pom import cloud.piranha.core piranha-core-impl ${project.version} compile cloud.piranha.feature piranha-feature-exitonstop ${project.version} compile cloud.piranha.feature piranha-feature-http ${project.version} compile cloud.piranha.feature piranha-feature-https ${project.version} compile cloud.piranha.feature piranha-feature-impl ${project.version} compile cloud.piranha.feature piranha-feature-logging ${project.version} compile cloud.piranha.feature piranha-feature-webapp ${project.version} compile cloud.piranha.http piranha-http-crac ${project.version} compile org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test org.apache.maven.plugins maven-javadoc-plugin true org.jacoco jacoco-maven-plugin default-check check BUNDLE INSTRUCTION COVEREDRATIO 0.07 CLASS MISSEDCOUNT 3 ================================================ FILE: single/src/main/java/cloud/piranha/single/SingleMain.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.single; import static java.lang.System.Logger.Level.WARNING; import java.lang.System.Logger; import java.lang.System.Logger.Level; /** * The Single version of Main. * * @author Manfred Riem (mriem@manorrock.com) */ public class SingleMain { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(SingleMain.class.getName()); /** * Constructor. */ public SingleMain() { } /** * Main method. * * @param arguments the arguments. */ public static void main(String[] arguments) { SinglePiranhaBuilder builder = new SingleMain().processArguments(arguments); if (builder != null) { builder.build().start(); } else { showHelp(); } } /** * Process the arguments. * * @param arguments the arguments. * @return this. */ protected SinglePiranhaBuilder processArguments(String[] arguments) { SinglePiranhaBuilder builder = new SinglePiranhaBuilder() .exitOnStop(true); int httpPort = 0; int httpsPort = 0; if (arguments != null) { for (int i = 0; i < arguments.length; i++) { if (arguments[i].equals("--enable-crac")) { builder = builder.crac(true); } if (arguments[i].equals("--extension-class")) { builder = builder.extensionClass(arguments[i + 1]); } if (arguments[i].equals("--context-path")) { builder = builder.contextPath(arguments[i + 1]); } if (arguments[i].equals("--help")) { return null; } if (arguments[i].equals("--http-port")) { int arg = Integer.parseInt(arguments[i + 1]); builder = builder.httpPort(arg); httpPort = arg; } if (arguments[i].equals("--http-server-class")) { builder = builder.httpServerClass(arguments[i + 1]); } if (arguments[i].equals("--https-keystore-file")) { builder = builder.httpsKeystoreFile(arguments[i + 1]); } if (arguments[i].equals("--https-keystore-password")) { builder = builder.httpsKeystorePassword(arguments[i + 1]); } if (arguments[i].equals("--https-port")) { int arg = Integer.parseInt(arguments[i + 1]); builder = builder.httpsPort(arg); httpsPort = arg; } if (arguments[i].equals("--https-server-class")) { builder = builder.httpsServerClass(arguments[i + 1]); } if (arguments[i].equals("--https-truststore-file")) { builder = builder.httpsTruststoreFile(arguments[i + 1]); } if (arguments[i].equals("--https-truststore-password")) { builder = builder.httpsTruststorePassword(arguments[i + 1]); } if (arguments[i].equals("--jpms")) { builder = builder.jpms(true); } if (arguments[i].equals("--logging-level")) { builder = builder.loggingLevel(arguments[i + 1]); } if (arguments[i].equals("--verbose")) { builder = builder.verbose(true); } if (arguments[i].equals("--war-file")) { builder = builder.warFile(arguments[i + 1]); } if (arguments[i].equals("--webapp-dir")) { builder = builder.webAppDir(arguments[i + 1]); } if (arguments[i].equals("--write-pid")) { builder = builder.pid(ProcessHandle.current().pid()); } } checkPorts(httpPort, httpsPort); } return builder; } /** * Check the HTTP and HTTPS port. * * @param httpPort the HTTP port. * @param httpsPort the HTTPS port. */ private void checkPorts(int httpPort, int httpsPort) { if(httpsPort != 0 && httpPort == httpsPort) { LOGGER.log(WARNING, "The http and the https ports are the same. Please use different ports"); System.exit(-1); } } /** * Show help. */ protected static void showHelp() { LOGGER.log(Level.INFO, ""); LOGGER.log(Level.INFO, """ --enable-crac - Enable Project CRaC support --extension-class - Set the extension to use --context-path - Set the Servlet context path --help - Show this help --http-port - Set the HTTP port (use -1 to disable) --http-server-class - Set the HTTP server class to use --https-keystore-file - Set the HTTPS keystore file (applies to the whole JVM) --https-keystore-password - Set the HTTPS keystore password (applies to the whole JVM) --https-port - Set the HTTPS port (disabled by default) --https-server-class - Set the HTTPS server class to use --https-truststore-file - Set the HTTPS keystore file (applies to the whole JVM) --https-truststore-password - Set the HTTPS keystore password (applies to the whole JVM) --jpms - Enable Java Platform Module System --logging-level - Set the java.util.logging.Level --verbose - Shows the runtime parameters --war-file - The WAR file to deploy --webapp-dir - The directory to use for the web application (auto creates when it does not exist, if omitted runtime will use the filename portion of --war-file) --write-pid - Write out a PID file """); } } ================================================ FILE: single/src/main/java/cloud/piranha/single/SinglePiranha.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.single; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; import cloud.piranha.core.api.Piranha; import cloud.piranha.core.api.PiranhaConfiguration; import cloud.piranha.core.api.WebApplicationExtension; import cloud.piranha.core.impl.DefaultPiranhaConfiguration; import cloud.piranha.feature.api.FeatureManager; import cloud.piranha.feature.exitonstop.ExitOnStopFeature; import cloud.piranha.feature.http.HttpFeature; import cloud.piranha.feature.https.HttpsFeature; import cloud.piranha.feature.impl.DefaultFeatureManager; import cloud.piranha.feature.logging.LoggingFeature; import cloud.piranha.feature.webapp.WebAppFeature; import cloud.piranha.http.api.HttpServer; import static java.lang.System.Logger.Level.ERROR; import static java.lang.System.Logger.Level.INFO; import static java.lang.System.Logger.Level.WARNING; /** * The Single version of Piranha. * *

* This version of Piranha supports the following: *

*
    *
  1. Enabling Project CRaC
  2. *
  3. Running with Java modules
  4. *
  5. Exiting on stop
  6. *
  7. Exposing a HTTP endpoint
  8. *
  9. Exposing a HTTPS endpoint
  10. *
  11. Setting the logging level
  12. *
  13. Hosting a single web application
  14. *
* * @author Manfred Riem (mriem@manorrock.com) */ public class SinglePiranha implements Piranha, Runnable { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(SinglePiranha.class.getName()); /** * Stores the configuration. */ protected final PiranhaConfiguration configuration; /** * Stores the feature manager. */ protected final FeatureManager featureManager; /** * Stores the stop flag. */ protected boolean stop = false; /** * Stores the thread we use. */ protected Thread thread; /** * Constructor. */ public SinglePiranha() { configuration = new DefaultPiranhaConfiguration(); configuration.setBoolean("exitOnStop", false); configuration.setBoolean("cracEnabled", false); configuration.setInteger("httpPort", 8080); configuration.setInteger("httpsPort", -1); configuration.setBoolean("jpmsEnabled", false); featureManager = new DefaultFeatureManager(); } @Override public PiranhaConfiguration getConfiguration() { return configuration; } /** * Run method. */ @SuppressWarnings("unchecked") @Override public void run() { long startTime = System.currentTimeMillis(); LoggingFeature loggingFeature = new LoggingFeature(); loggingFeature.setLevel(configuration.getString("loggingLevel")); loggingFeature.init(); loggingFeature.start(); featureManager.addFeature(loggingFeature); LOGGER.log(INFO, () -> "Starting Piranha"); WebAppFeature webAppFeature = new WebAppFeature(); featureManager.addFeature(webAppFeature); webAppFeature.setContextPath(configuration.getString("contextPath")); webAppFeature.setExtensionClass((Class) configuration.getClass("extensionClass")); webAppFeature.setJpmsEnabled(configuration.getBoolean("jpmsEnabled", false)); webAppFeature.setWarFile(configuration.getFile("warFile")); webAppFeature.setWebAppDir(configuration.getFile("webAppDir")); webAppFeature.init(); webAppFeature.start(); featureManager.addFeature(webAppFeature); /* * Construct, initialize and start HTTP endpoint (if applicable). */ if (configuration.getInteger("httpPort") > 0) { HttpFeature httpFeature = new HttpFeature(); httpFeature.setHttpServerClass(configuration.getString("httpServerClass")); httpFeature.setPort(configuration.getInteger("httpPort")); httpFeature.init(); httpFeature.getHttpServer().setHttpServerProcessor(webAppFeature.getHttpServerProcessor()); /* * Enable Project CRaC. */ if (configuration.getBoolean("cracEnabled", false)) { HttpServer httpServer = httpFeature.getHttpServer(); try { HttpServer cracHttpServer = (HttpServer) Class .forName("cloud.piranha.http.crac.CracHttpServer") .getDeclaredConstructor(HttpServer.class) .newInstance(httpServer); httpServer = cracHttpServer; } catch (ClassNotFoundException | IllegalAccessException | IllegalArgumentException | InstantiationException | NoSuchMethodException | SecurityException | InvocationTargetException t) { LOGGER.log(ERROR, "Unable to construct HTTP server", t); } httpFeature.setHttpServer(httpServer); } httpFeature.start(); featureManager.addFeature(httpFeature); } /* * Construct, initialize and start HTTPS endpoint (if applicable). */ if (configuration.getInteger("httpsPort") > 0) { HttpsFeature httpsFeature = new HttpsFeature(); httpsFeature.setHttpsKeystoreFile(configuration.getString("httpsKeystoreFile")); httpsFeature.setHttpsKeystorePassword(configuration.getString("httpsKeystorePassword")); httpsFeature.setHttpsServerClass(configuration.getString("httpsServerClass")); httpsFeature.setHttpsTruststoreFile(configuration.getString("httpsTruststoreFile")); httpsFeature.setHttpsTruststorePassword(configuration.getString("httpsTruststorePassword")); httpsFeature.setPort(configuration.getInteger("httpsPort")); httpsFeature.init(); httpsFeature.getHttpsServer().setHttpServerProcessor(webAppFeature.getHttpServerProcessor()); /* * Enable Project CRaC. */ if (configuration.getBoolean("cracEnabled", false)) { HttpServer httpsServer = httpsFeature.getHttpsServer(); try { HttpServer cracHttpServer = (HttpServer) Class .forName("cloud.piranha.http.crac.CracHttpServer") .getDeclaredConstructor(HttpServer.class) .newInstance(httpsServer); httpsServer = cracHttpServer; } catch (ClassNotFoundException | IllegalAccessException | IllegalArgumentException | InstantiationException | NoSuchMethodException | SecurityException | InvocationTargetException t) { LOGGER.log(ERROR, "Unable to construct HTTPS server", t); } httpsFeature.setHttpsServer(httpsServer); } httpsFeature.start(); featureManager.addFeature(httpsFeature); } if (configuration.getBoolean("exitOnStop", false)) { ExitOnStopFeature exitOnStopFeature = new ExitOnStopFeature(); featureManager.addFeature(exitOnStopFeature); } if (configuration.getLong("pid") != null) { File pidFile = new File("tmp", "piranha.pid"); if (!pidFile.getParentFile().exists() && !pidFile.getParentFile().mkdirs()) { LOGGER.log(WARNING, "Unable to create tmp directory for PID file"); } try (PrintWriter writer = new PrintWriter(new FileWriter(pidFile))) { writer.println(configuration.getLong("pid")); writer.flush(); } catch (IOException ioe) { LOGGER.log(WARNING, "Unable to write PID file", ioe); } } long finishTime = System.currentTimeMillis(); LOGGER.log(INFO, "Started Piranha"); LOGGER.log(INFO, "It took {0} milliseconds", finishTime - startTime); while (!stop) { if (configuration.getLong("pid") != null) { File pidFile = new File("tmp", "piranha.pid"); if (!pidFile.exists()) { LOGGER.log(INFO, "Stopping Piranha"); startTime = System.currentTimeMillis(); stop(); finishTime = System.currentTimeMillis(); LOGGER.log(INFO, "Stopped Piranha\n It took {0} milliseconds", finishTime - startTime); break; } } try { Thread.sleep(1000); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } } /** * Start the server. */ public void start() { thread = new Thread(this); thread.start(); } /** * Stop the server. */ public void stop() { stop = true; thread = null; featureManager.stop(); } } ================================================ FILE: single/src/main/java/cloud/piranha/single/SinglePiranhaBuilder.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.single; import cloud.piranha.core.api.PiranhaBuilder; import cloud.piranha.core.api.PiranhaConfiguration; import cloud.piranha.core.api.WebApplicationExtension; import java.io.File; import static java.lang.System.Logger.Level.WARNING; /** * The Single version of PiranhaBuilder. * * @author Manfred Riem (mriem@manorrock.com) */ public class SinglePiranhaBuilder implements PiranhaBuilder { /** * Stores the logger. */ private static final System.Logger LOGGER = System.getLogger(SinglePiranhaBuilder.class.getName()); /** * Stores the SinglePiranha instance. */ private final SinglePiranha piranha; /** * Stores the verbose flag. */ private boolean verbose = false; /** * Constructor. */ public SinglePiranhaBuilder() { piranha = new SinglePiranha(); } @Override public SinglePiranha build() { if (verbose) { showArguments(); } return piranha; } /** * Set the context path. * * @param contextPath the context path. * @return the builder. */ public SinglePiranhaBuilder contextPath(String contextPath) { piranha.getConfiguration().setString("contextPath", contextPath); return this; } /** * Set the CRaC enabled flag. * * @param crac the CRaC enabled flag. * @return the builder. */ public SinglePiranhaBuilder crac(boolean crac) { piranha.getConfiguration().setBoolean("cracEnabled", crac); return this; } /** * Set the exit on stop flag. * * @param exitOnStop the exit on stop flag. * @return the builder. */ public SinglePiranhaBuilder exitOnStop(boolean exitOnStop) { piranha.getConfiguration().setBoolean("exitOnStop", exitOnStop); return this; } /** * Set the extension class. * * @param extensionClass the extension class. * @return the builder. */ public SinglePiranhaBuilder extensionClass( Class extensionClass) { piranha.getConfiguration().setClass("extensionClass", extensionClass); return this; } /** * Set the extension class. * * @param extensionClassName the default extension class name. * @return the builder. */ public SinglePiranhaBuilder extensionClass(String extensionClassName) { try { piranha.getConfiguration().setClass("extensionClass", (Class) Class.forName(extensionClassName)); } catch (ClassNotFoundException cnfe) { LOGGER.log(WARNING, "Unable to load extension class", cnfe); } return this; } /** * Get the configuration. * * @return the configuration. */ public PiranhaConfiguration getConfiguration() { return piranha.getConfiguration(); } /** * Set the HTTP server port. * * @param httpPort the HTTP server port. * @return the builder. */ public SinglePiranhaBuilder httpPort(int httpPort) { piranha.getConfiguration().setInteger("httpPort", httpPort); return this; } /** * Set the HTTP server class. * * @param httpServerClass the HTTP server class. * @return the builder. */ public SinglePiranhaBuilder httpServerClass(String httpServerClass) { piranha.getConfiguration().setString("httpServerClass", httpServerClass); return this; } /** * Set the HTTPS keystore file. * * @param httpsKeystoreFile the HTTPS keystore file. * @return the builder. */ public SinglePiranhaBuilder httpsKeystoreFile(String httpsKeystoreFile) { piranha.getConfiguration().setString("httpsKeystoreFile", httpsKeystoreFile); return this; } /** * Set the HTTPS keystore password. * * @param httpsKeystorePassword the HTTPS keystore password. * @return the builder. */ public SinglePiranhaBuilder httpsKeystorePassword(String httpsKeystorePassword) { piranha.getConfiguration().setString("httpsKeystorePassword", httpsKeystorePassword); return this; } /** * Set the HTTPS server port. * * @param httpsPort the HTTPS server port. * @return the builder. */ public SinglePiranhaBuilder httpsPort(int httpsPort) { piranha.getConfiguration().setInteger("httpsPort", httpsPort); return this; } /** * Set the HTTPS server class. * * @param httpsServerClass the HTTPS server class. * @return the builder. */ public SinglePiranhaBuilder httpsServerClass(String httpsServerClass) { piranha.getConfiguration().setString("httpsServerClass", httpsServerClass); return this; } /** * Set the HTTPS truststore file. * * @param httpsTruststoreFile the HTTPS truststore file. * @return the builder. */ public SinglePiranhaBuilder httpsTruststoreFile(String httpsTruststoreFile) { piranha.getConfiguration().setString("httpsTruststoreFile", httpsTruststoreFile); return this; } /** * Set the HTTPS truststore password. * * @param httpsTruststorePassword the HTTPS truststore password. * @return the builder. */ public SinglePiranhaBuilder httpsTruststorePassword(String httpsTruststorePassword) { piranha.getConfiguration().setString("httpsTruststorePassword", httpsTruststorePassword); return this; } /** * Enable/disable JPMS. * * @param jpms the JPMS flag. * @return the builder. */ public SinglePiranhaBuilder jpms(boolean jpms) { piranha.getConfiguration().setBoolean("jpmsEnabled", jpms); return this; } /** * Set the logging level. * * @param loggingLevel the logging level. * @return the builder. */ public SinglePiranhaBuilder loggingLevel(String loggingLevel) { piranha.getConfiguration().setString("loggingLevel", loggingLevel); return this; } /** * Set the PID. * * @param pid the PID. * @return the builder. */ public SinglePiranhaBuilder pid(Long pid) { piranha.getConfiguration().setLong("pid", pid); return this; } /** * Show the arguments used. */ private void showArguments() { PiranhaConfiguration configuration = piranha.getConfiguration(); LOGGER.log(System.Logger.Level.INFO, """ PIRANHA Arguments ========= Context path : %s Extension class : %s Exit on stop : %s HTTP port : %s HTTP server class : %s HTTPS keystore file : %s HTTPS keystore password : **** HTTPS port : %s HTTPS server class : %s HTTPS truststore file : %s HTTPS truststore password : **** JPMS enabled : %s Logging level : %s PID : %s WAR filename : %s Web application dir : %s """.formatted( configuration.getString("contextPath"), configuration.getClass("extensionClass"), configuration.getBoolean("exitOnStop", false), configuration.getInteger("httpPort"), configuration.getString("httpServerClass"), configuration.getString("httpsKeystoreFile"), configuration.getInteger("httpsPort"), configuration.getString("httpsServerClass"), configuration.getString("httpsTruststoreFile"), configuration.getBoolean("jpms", false), configuration.getString("loggingLevel"), configuration.getLong("pid"), configuration.getFile("warFile"), configuration.getFile("webAppDir") ) ); } /** * Set the verbose flag. * * @param verbose the verbose flag. * @return the builder. */ public SinglePiranhaBuilder verbose(boolean verbose) { this.verbose = verbose; return this; } /** * Set the WAR file. * * @param warFile the WAR file. * @return the builder. */ public SinglePiranhaBuilder warFile(String warFile) { piranha.getConfiguration().setFile("warFile", new File(warFile)); return this; } /** * Set the web application directory. * * @param webAppDir the web application directory. * @return the builder. */ public SinglePiranhaBuilder webAppDir(String webAppDir) { piranha.getConfiguration().setFile("webAppDir", new File(webAppDir)); return this; } } ================================================ FILE: single/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers Piranha Single. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.single { exports cloud.piranha.single; opens cloud.piranha.single; requires transitive cloud.piranha.core.impl; requires cloud.piranha.feature.exitonstop; requires cloud.piranha.feature.http; requires cloud.piranha.feature.https; requires cloud.piranha.feature.impl; requires cloud.piranha.feature.logging; requires cloud.piranha.feature.webapp; requires cloud.piranha.http.crac; requires cloud.piranha.http.webapp; requires jakarta.servlet; } ================================================ FILE: single/src/test/java/cloud/piranha/single/SinglePiranhaBuilderTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.single; import java.net.ConnectException; import java.net.Socket; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.Test; /** * The JUnit tests for the SinglePiranhaBuilder class. * * @author Manfred Riem (mriem@manorrock.com) */ class SinglePiranhaBuilderTest { /** * Test extensionClass method. * * @throws Exception when a serious error occurs. */ @Test void testExtensionClass() throws Exception { SinglePiranha piranha = new SinglePiranhaBuilder() .httpPort(8080) .verbose(true) .build(); piranha.start(); Thread.sleep(5000); try ( Socket socket = new Socket("localhost", 8080)) { assertNotNull(socket.getOutputStream()); } catch (ConnectException e) { } piranha.stop(); Thread.sleep(5000); } /** * Test extensionClass method. * * @throws Exception when a serious error occurs. */ @Test void testExtensionClass2() throws Exception { SinglePiranha piranha = new SinglePiranhaBuilder() .httpPort(8081) .verbose(true) .build(); piranha.start(); Thread.sleep(5000); try ( Socket socket = new Socket("localhost", 8081)) { assertNotNull(socket.getOutputStream()); } catch (ConnectException e) { } piranha.stop(); Thread.sleep(5000); } } ================================================ FILE: single/src/test/java/cloud/piranha/single/SinglePiranhaTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.single; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; /** * The JUnit tests for the SinglePiranha class. * * @author Manfred Riem (mriem@manorrock.com) */ class SinglePiranhaTest { /** * Test getConfiguration method. */ @Test void testGetConfiguration() { SinglePiranha piranha = new SinglePiranha(); assertNotNull(piranha.getConfiguration()); } } ================================================ FILE: spring/pom.xml ================================================ 4.0.0 cloud.piranha project 25.4.0-SNAPSHOT cloud.piranha.spring project pom Piranha - Spring spring-boot-starter-piranha-embedded cloud.piranha bom ${project.version} pom import org.springframework.boot spring-boot-dependencies ${spring-boot.version} pom import org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test default file:///tmp/piranha/spring/ ================================================ FILE: spring/spring-boot-starter-piranha-embedded/pom.xml ================================================ 4.0.0 cloud.piranha.spring project 25.4.0-SNAPSHOT piranha-embedded-spring-boot-starter jar Piranha - Spring - Piranha Embedded Spring Boot Starter This module delivers the Spring Boot starter for Piranha Embedded. cloud.piranha piranha-embedded ${project.version} cloud.piranha.http piranha-http-impl ${project.version} cloud.piranha.http piranha-http-webapp ${project.version} org.springframework.boot spring-boot ================================================ FILE: spring/spring-boot-starter-piranha-embedded/src/main/java/cloud/piranha/spring/starter/embedded/EmbeddedPiranhaServletWebServerFactory.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.spring.starter.embedded; import cloud.piranha.http.api.HttpServer; import org.springframework.boot.web.server.WebServer; import org.springframework.boot.web.servlet.ServletContextInitializer; import org.springframework.boot.web.servlet.server.ServletWebServerFactory; /** * The Piranha Embedded ServletWebServerFactory. * * @author Manfred Riem (mriem@manorrock.com) */ public class EmbeddedPiranhaServletWebServerFactory implements ServletWebServerFactory { /** * Stores the context path. */ private String contextPath = ""; /** * Stores the port. */ private int port = 8080; /** * Stores the HTTP server implementation */ private HttpServer httpServer; /** * Constructor. */ public EmbeddedPiranhaServletWebServerFactory() { } @Override public WebServer getWebServer(ServletContextInitializer... initializers) { EmbeddedPiranhaWebServer webServer = new EmbeddedPiranhaWebServer(); webServer.setContextPath(contextPath); webServer.setPort(port); webServer.setInitializers(initializers); webServer.setHttpServer(httpServer); webServer.init(); return webServer; } /** * Set the context path. * * @param contextPath the context path. */ public void setContextPath(String contextPath) { this.contextPath = contextPath; } /** * Set the port. * * @param port the port. */ public void setPort(int port) { this.port = port; } /** * Set the HTTP server implementation * @param httpServer the HTTP server implementation */ public void setHttpServer(HttpServer httpServer) { this.httpServer = httpServer; } } ================================================ FILE: spring/spring-boot-starter-piranha-embedded/src/main/java/cloud/piranha/spring/starter/embedded/EmbeddedPiranhaWebServer.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.spring.starter.embedded; import cloud.piranha.embedded.EmbeddedPiranha; import cloud.piranha.http.api.HttpServer; import cloud.piranha.http.impl.DefaultHttpServer; import cloud.piranha.http.webapp.HttpWebApplicationServer; import jakarta.servlet.ServletException; import org.springframework.boot.web.server.WebServer; import org.springframework.boot.web.server.WebServerException; import org.springframework.boot.web.servlet.ServletContextInitializer; /** * The Piranha Embedded WebServer. * * @author Manfred Riem (mriem@manorrock.com) */ public class EmbeddedPiranhaWebServer implements WebServer { /** * Stores the context path. */ private String contextPath = ""; /** * Stores the HttpServer instance. */ private HttpServer httpServer; /** * Stores the initializers. */ private ServletContextInitializer[] initializers; /** * Stores the port. */ private int port = 8080; /** * Constructor. */ public EmbeddedPiranhaWebServer() { } @Override public int getPort() { return port; } /** * Initialize. */ public void init() { EmbeddedPiranha piranha = new EmbeddedPiranha(); if (initializers != null) { for (ServletContextInitializer initializer : initializers) { try { initializer.onStartup(piranha.getWebApplication()); } catch (ServletException se) { throw new RuntimeException(se); } } } piranha.initialize(); piranha.start(); HttpWebApplicationServer server = new HttpWebApplicationServer(); server.start(); piranha.getWebApplication().setContextPath(contextPath); server.addWebApplication(piranha.getWebApplication()); if (httpServer == null) httpServer = new DefaultHttpServer(port); httpServer.setHttpServerProcessor(server); } /** * Set the context path. * * @param contextPath the context path. */ public void setContextPath(String contextPath) { this.contextPath = contextPath; } /** * Set the initializers. * * @param initializers the initializers. */ public void setInitializers(ServletContextInitializer[] initializers) { this.initializers = initializers; } /** * Set the port. * * @param port the server port. */ public void setPort(int port) { this.port = port; } /** * Set the HTTP server implementation. * * @param httpServer the HTTP server implementation. */ public void setHttpServer(HttpServer httpServer) { this.httpServer = httpServer; } @Override public void start() throws WebServerException { httpServer.start(); } @Override public void stop() throws WebServerException { httpServer.stop(); } } ================================================ FILE: spring/spring-boot-starter-piranha-embedded/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * This module delivers the Piranha Embedded Spring Boot starter. * * @author Manfred Riem (mriem@manorrock.com) */ module cloud.piranha.spring.starter.embedded { exports cloud.piranha.spring.starter.embedded; opens cloud.piranha.spring.starter.embedded; requires cloud.piranha.embedded; requires cloud.piranha.http.impl; requires cloud.piranha.http.webapp; requires transitive cloud.piranha.http.api; requires transitive spring.boot; } ================================================ FILE: src/site/markdown/index.md ================================================ # Getting Started ## Quickstart using Piranha Web Profile Download a version of Piranha Web Profile from [Maven Central](https://repo1.maven.org/maven2/cloud/piranha/dist/piranha-dist-webprofile/). Run your web application using: ```shell java -jar piranha-webprofile-X.Y.Z.jar --war-file your-webapplication.war ``` ## What other distributions can I use? The short answer is that it depends on your needs. If you want to embed Piranha into your application you can pick Piranha Embedded to start embedding Piranha directly into your own application. If you are looking for a lean REST runtime then you should pick Piranha Core Profile. If you are looking for a traditional Servlet container, similar to Tomcat and Jetty, then Piranha Server or Piranha Servlet are your best bet. For hosting multiple web applications on the same distribution use Piranha Server. For hosting just a single web application pick Piranha Servlet. If you are looking for a Web Profile capable runtime, similar to Glassfish, Payara, WebLogic or WebSphere, then Piranha Web Profile is your best bet. ## Distribution specific documentation * [Piranha Core Profile](dist/coreprofile/index.html) * [Piranha Embedded](embedded/index.html) * [Piranha Servlet](dist/servlet/index.html) * [Piranha Server](dist/server/index.html) * [Piranha Web Profile](dist/webprofile/index.html) ## Maven Plugin documentation If you are using Maven and want to use our convenient Maven plugin for a quick iterative development cycle, see the [Piranha Maven plugin documentation](maven/plugin/plugin-info.html). ## Extending Piranha You can extend the capability of a Piranha distribution by means of extensions. ## How do you run Piranha in a container Piranha images are available from the [GitHub container registry](https://github.com/orgs/piranhacloud/packages), on a best effort basis. ## Commercial support If you desire commercial support we suggest you contact our commercial partner [OmniFish](https://omnifish.ee/contact-us/). ================================================ FILE: src/site/site.xml ================================================ Piranha https://piranha.cloud/ A cloud native extensible runtime pull-right piranhacloud/piranha left-bottom black piranha_cloud org.apache.maven.skins maven-fluido-skin 2.0.0 ================================================ FILE: test/common/pom.xml ================================================ 4.0.0 cloud.piranha.test project 25.4.0-SNAPSHOT test-common Piranha - Test - Common functionality ================================================ FILE: test/common/src/main/java/cloud/piranha/test/common/PiranhaStartup.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.common; import java.io.IOException; import java.lang.System.Logger.Level; import static java.lang.System.Logger.Level.DEBUG; import java.net.Socket; import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; /** * * @author Ondro Mihalyi */ public class PiranhaStartup { /** * Wait until Piranha process is ready and opens the port. * @param process * @param port */ public static void waitUntilPiranhaReady(Process process, int port) { waitUntilPiranhaReady(port, process::isAlive); } /** * Wait until Piranha is ready and opens the port. * @param port */ public static void waitUntilPiranhaReady(int port) { waitUntilPiranhaReady(port, () -> true); } private static void waitUntilPiranhaReady(int port, BooleanSupplier customCheck) { final int timeoutMillis = 600 * 1000; final int sleepTimeMillis = 1000; long initialTimeMillis = System.currentTimeMillis(); boolean started = false; while (customCheck.getAsBoolean() && !started) { if (initialTimeMillis + timeoutMillis < System.currentTimeMillis()) { // timeout reached throw new RuntimeException("Piranha wasn't ready before timeout reached. Timeout is " + timeoutMillis + " milliseconds."); } try { TimeUnit.MILLISECONDS.sleep(sleepTimeMillis); pingPiranha("localhost", port); started = true; } catch (IOException ex) { System.getLogger(PiranhaStartup.class.getName()).log( DEBUG, "Not ready yet: {0}{1}", ex.getMessage(), ex.getClass()); } catch (InterruptedException ex) { System.getLogger(PiranhaStartup.class.getName()).log(Level.WARNING, ex.getMessage(), ex); Thread.currentThread().interrupt(); } } if (!customCheck.getAsBoolean()) { throw new RuntimeException("Piranha Micro failed to start"); } // wait some more time just in case the port is already bound to but Piranha isn't ready to serve requests yet try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException ex) { throw new RuntimeException(ex); } } private static void pingPiranha(String host, int port) throws IOException { try ( Socket s = new Socket(host, port)) { } } } ================================================ FILE: test/common/src/main/java/module-info.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ module cloud.piranha.test.common { exports cloud.piranha.test.common; } ================================================ FILE: test/coreprofile/arquillian/pom.xml ================================================ 4.0.0 cloud.piranha.test.coreprofile project 25.4.0-SNAPSHOT piranha-test-coreprofile-arquillian war Piranha - Test - Core Profile - Testing with JUnit 5 and Arquillian UTF-8 jakarta.platform jakarta.jakartaee-core-api provided cloud.piranha.arquillian piranha-arquillian-managed ${project.version} test org.jboss.arquillian.junit5 arquillian-junit5-container ${arquillian.version} test org.junit.jupiter junit-jupiter test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test arquillian org.apache.maven.plugins maven-compiler-plugin ${java.version} org.apache.maven.plugins maven-failsafe-plugin integration-test integration-test integration-test verify org.apache.maven.plugins maven-war-plugin false ================================================ FILE: test/coreprofile/arquillian/src/main/java/arquillian/HelloArquillianApplication.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package arquillian; import jakarta.ws.rs.ApplicationPath; import jakarta.ws.rs.core.Application; @ApplicationPath("") public class HelloArquillianApplication extends Application { } ================================================ FILE: test/coreprofile/arquillian/src/main/java/arquillian/HelloArquillianBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package arquillian; import jakarta.enterprise.context.RequestScoped; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @Path("/helloarquillian") @RequestScoped public class HelloArquillianBean { /** * Get the 'Hello Arquillian' message. * * @return the message. */ @GET public String helloArquillian() { return "Hello Arquillian!"; } } ================================================ FILE: test/coreprofile/arquillian/src/test/java/helloarquillian/HelloArquillianIT.java ================================================ package helloarquillian; import arquillian.HelloArquillianApplication; import arquillian.HelloArquillianBean; import java.net.URI; import java.net.URL; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.RunAsClient; import org.jboss.arquillian.junit5.ArquillianExtension; import org.jboss.arquillian.test.api.ArquillianResource; import static org.jboss.shrinkwrap.api.ShrinkWrap.create; import org.jboss.shrinkwrap.api.spec.WebArchive; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @ExtendWith(ArquillianExtension.class) public class HelloArquillianIT { @ArquillianResource private URL baseUrl; @Deployment(testable = false) public static WebArchive createDeployment() { return create(WebArchive.class) .addClass(HelloArquillianApplication.class) .addClass(HelloArquillianBean.class); } @Test @RunAsClient public void testHelloArquillian() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI(baseUrl + "helloarquillian")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello Arquillian!")); } } ================================================ FILE: test/coreprofile/integration/pom.xml ================================================ 4.0.0 cloud.piranha.test.coreprofile project 25.4.0-SNAPSHOT piranha-test-coreprofile-integration war Piranha - Test - Core Profile - Distribution Integration Tests UTF-8 jakarta.platform jakarta.jakartaee-core-api provided org.glassfish.jersey.core jersey-client ${jersey.version} test org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test piranha-test-coreprofile-integration cloud.piranha.maven piranha-maven-plugin ${project.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop coreprofile ${httpPort} org.apache.maven.plugins maven-failsafe-plugin integration-test verify 10 ${httpPort} org.apache.maven.plugins maven-war-plugin false org.codehaus.mojo build-helper-maven-plugin reserve-network-port reserve-network-port validate httpPort debug cloud.piranha.maven piranha-maven-plugin ${project.version} -Xdebug -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=9009 ================================================ FILE: test/coreprofile/integration/src/main/java/cloud/piranha/test/coreprofile/distribution/AsyncBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; /** * The bean to test the Produces annotation. * * @author Manfred Riem (mriem@manorrock.com) */ @Path("async") public class AsyncBean { /** * Test to validate NOT_ACCEPTABLE (406) is returned. * * @return "This should not show up!" */ @TRACE @Path("notAcceptable") @Produces(TEXT_PLAIN) public String notAcceptable() { return "This should not show up!"; } } ================================================ FILE: test/coreprofile/integration/src/main/java/cloud/piranha/test/coreprofile/distribution/BeanParamBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import jakarta.ws.rs.BeanParam; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import static jakarta.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED; import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; import jakarta.ws.rs.core.Response; /** * The BeanParam bean to test the BeanParam annotation integration. * * @author Manfred Riem (mriem@manorrock.com) */ @Path("beanParam") public class BeanParamBean { /** * Process BeanParam annotated input without content body. * * @param input the input. * @return the response. */ @POST @Path("withoutContent") @Consumes(APPLICATION_FORM_URLENCODED) @Produces(TEXT_PLAIN) public Response beanParamInput(@BeanParam BeanParamInput input) { return Response.ok(input.toString()).build(); } /** * Process BeanParam annotated input with content body. * * @param content the content body. * @param input the input. * @return the response. */ @POST @Path("withContent") @Consumes(APPLICATION_FORM_URLENCODED) @Produces(TEXT_PLAIN) public Response beanParamInputWithContent(String content, @BeanParam BeanParamInput input) { return Response.ok(content + "," + input.toString()).build(); } } ================================================ FILE: test/coreprofile/integration/src/main/java/cloud/piranha/test/coreprofile/distribution/BeanParamInput.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.QueryParam; /** * A POJO for BeanParam annotation testing. * * @author Manfred Riem (mriem@manorrock.com) */ public class BeanParamInput { /** * A form parameter. */ @FormParam("formParam") private String formParam; /** * A query parameter. */ @QueryParam("queryParam") private int queryParam; /** * A header parameter. */ @HeaderParam("Content-Type") private String contentType; /** * Return string representation. * * @return the string representation. */ @Override public String toString() { return "UserInput{formParam='" + formParam + "', queryParam=" + queryParam + ", contentType='" + contentType + "'}"; } } ================================================ FILE: test/coreprofile/integration/src/main/java/cloud/piranha/test/coreprofile/distribution/ContainerRequestContextBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; /** * The bean to test the ContainerRequestContext integration works. * * @author Manfred Riem (mriem@manorrock.com) */ @Path("containerRequestContext") public class ContainerRequestContextBean { /** * Test to validate if the "accept" header with value "text/plain" is found. * * @return "This should not show up!" */ @GET @Path("containsHeaderString") @Produces(TEXT_PLAIN) public String containsHeaderString() { return System.getProperty("containsHeaderString"); } } ================================================ FILE: test/coreprofile/integration/src/main/java/cloud/piranha/test/coreprofile/distribution/ContainerRequestContextFilter.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import jakarta.annotation.Priority; import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.container.ContainerRequestFilter; import jakarta.ws.rs.container.PreMatching; import jakarta.ws.rs.ext.Provider; import java.io.IOException; /** * The ContainerRequestFilter used to validate we can access the * ContainerRequestContext and see the given header string. * * @author Manfred Riem (mriem@manorrock.com) */ @Provider @Priority(100) @PreMatching public class ContainerRequestContextFilter implements ContainerRequestFilter { @Override public void filter(ContainerRequestContext requestContext) throws IOException { boolean equals = requestContext.containsHeaderString("accept", "text/plain"::equals); System.setProperty("containsHeaderString", Boolean.toString(equals)); } } ================================================ FILE: test/coreprofile/integration/src/main/java/cloud/piranha/test/coreprofile/distribution/DependencyInjectionBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import jakarta.enterprise.context.ApplicationScoped; /** * The one and only Dependency Injection bean. * * @author Manfred Riem (mriem@manorrock.com) */ @ApplicationScoped public class DependencyInjectionBean { /** * Get the string to validate dependency injection works. * * @return the string. */ public String dependencyInjection() { return "Dependency Injection works!"; } } ================================================ FILE: test/coreprofile/integration/src/main/java/cloud/piranha/test/coreprofile/distribution/IntegrationApplication.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import jakarta.ws.rs.ApplicationPath; import jakarta.ws.rs.core.Application; /** * The HelloWorld application. * * @author Manfred Riem (mriem@manorrock.com) */ @ApplicationPath("") public class IntegrationApplication extends Application { } ================================================ FILE: test/coreprofile/integration/src/main/java/cloud/piranha/test/coreprofile/distribution/InterceptInterceptor.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import jakarta.interceptor.AroundInvoke; import jakarta.interceptor.Interceptor; import jakarta.interceptor.InvocationContext; /** * The intercept interceptor. * * @author Manfred Riem (mriem@manorrock.com) */ @Interceptor public class InterceptInterceptor { /** * Intercept the call and return 'Interceptor works!'. * * @param context the invocation context. * @return 'Interceptor works!'. */ @AroundInvoke public Object intercepted(InvocationContext context) { return "Interceptor works!"; } } ================================================ FILE: test/coreprofile/integration/src/main/java/cloud/piranha/test/coreprofile/distribution/InterceptedBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import jakarta.enterprise.context.ApplicationScoped; import jakarta.interceptor.Interceptors; /** * The Intercepted bean. * * @author Manfred Riem (mriem@manorrock.com) */ @ApplicationScoped @Interceptors(InterceptInterceptor.class) public class InterceptedBean { /** * Get the string to validate the interceptor works. * * @return the string. */ public String intercept() { return "Inceptor does NOT work!"; } } ================================================ FILE: test/coreprofile/integration/src/main/java/cloud/piranha/test/coreprofile/distribution/Jsonb.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; /** * The JSON Binding POJO. * * @author Manfred Riem (mriem@manorrock.com) */ public class Jsonb { /** * Stores the string. */ private String string = "JSON Binding works!"; /** * Get the string. * * @return the string. */ public String getString() { return string; } /** * Set the string. * * @param string the string. */ public void setString(String string) { this.string = string; } } ================================================ FILE: test/coreprofile/integration/src/main/java/cloud/piranha/test/coreprofile/distribution/ProducesBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; /** * The bean to test the Produces annotation. * * @author Manfred Riem (mriem@manorrock.com) */ @Path("produces") public class ProducesBean { /** * Test to validate NOT_ACCEPTABLE (406) is returned. * * @return "This should not show up!" */ @GET @Path("notAcceptable") @Produces(TEXT_PLAIN) public String notAcceptable() { return "This should not show up!"; } } ================================================ FILE: test/coreprofile/integration/src/main/java/cloud/piranha/test/coreprofile/distribution/RestBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Inject; import jakarta.json.Json; import jakarta.json.stream.JsonParser; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import java.io.StringReader; /** * The Jsonb bean. * * @author Manfred Riem (mriem@manorrock.com) */ @Path("") @RequestScoped public class RestBean { /** * Stores the DependencyInjectionBean. */ @Inject private DependencyInjectionBean dependencyInjection; /** * Stores the intercept bean. */ @Inject private InterceptedBean interceptedBean; /** * Validate the correct string is returned using the bean injected using the * Inject annotation. * * @return 'Dependency Injection works!'. */ @GET @Path("/dependencyInjection") public String dependencyInjection() { return dependencyInjection.dependencyInjection(); } /** * Validate the correct string is returned using an interceptor. * * @return 'Interceptor work!'. */ @GET @Path("/intercept") public String intercept() { return interceptedBean.intercept(); } /** * Validate JSON Binding works. * * @return 'JSON Binding works!' in JSON format. */ @GET @Produces("application/json") @Path("/jsonb") public Jsonb jsonb() { return new Jsonb(); } /** * Post 'Hello Json-P!' in JSON format which gets parsed using JSON-P. * * @param jsonString a JSON string. * @return 'JSON Processing works!' in JSON format. */ @POST @Produces("application/json") @Consumes("application/json") @Path("/jsonp") public Jsonb helloJsonP(String jsonString) { Jsonb jsonb = new Jsonb(); try ( JsonParser parser = Json.createParser(new StringReader(jsonString));) { parser.next(); String string = parser.getString(); jsonb.setString(string); } return jsonb; } /** * Say 'REST works!'. * * @return 'Hello World!'. */ @GET @Path("/rest") public String rest() { return "REST works!"; } } ================================================ FILE: test/coreprofile/integration/src/main/java/cloud/piranha/test/coreprofile/distribution/SseBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.core.Context; import static jakarta.ws.rs.core.MediaType.SERVER_SENT_EVENTS; import jakarta.ws.rs.sse.OutboundSseEvent; import jakarta.ws.rs.sse.Sse; import jakarta.ws.rs.sse.SseEventSink; import java.io.IOException; /** * The bean to test the Server Side Event (SSE) integration works. * * @author Manfred Riem (mriem@manorrock.com) */ @Path("/sse") public class SseBean { /** * Stores the broadcaster. */ @Inject private SseBroadcastBean broadcastBean; /** * Stores the SSE context. */ @Context private Sse sse; /** * Test string based SSE. * * @param eventSink the event sink. */ @Path("string") @GET @Produces(SERVER_SENT_EVENTS) public void string(@Context SseEventSink eventSink) { new Thread(() -> { try (SseEventSink sink = eventSink; eventSink) { for (int i = 0; i < 5; i++) { Thread.sleep(1000); OutboundSseEvent event = sse.newEventBuilder(). name("string"). data(String.class, "Event " + i).build(); sink.send(event); } } catch (InterruptedException | IOException e) { throw new WebApplicationException(e); } }).start(); } /** * Perform a SSE Broadcast. * * @param message the message to broadcast. */ @Path("broadcast") @POST public void broadcast(String message) { broadcastBean.broadcast("Message"); } /** * Close the broadcast. */ @Path("close") @GET public void close() { broadcastBean.close(); } /** * Register to receive messages. * * @param eventSink the event sink. */ @Path("register") @GET @Produces(SERVER_SENT_EVENTS) public void register(@Context SseEventSink eventSink) { broadcastBean.register(eventSink); } } ================================================ FILE: test/coreprofile/integration/src/main/java/cloud/piranha/test/coreprofile/distribution/SseBroadcastBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import jakarta.enterprise.context.ApplicationScoped; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.sse.OutboundSseEvent; import jakarta.ws.rs.sse.Sse; import jakarta.ws.rs.sse.SseBroadcaster; import jakarta.ws.rs.sse.SseEventSink; /** * The single and one and only SSE broadcast bean. * * @author Manfred Riem (mriem@manorrock.com) */ @ApplicationScoped public class SseBroadcastBean { /** * Stores the broadcaster. */ private SseBroadcaster broadcaster; /** * Store the SSE. */ @Context private Sse sse; /** * Constructor. */ public SseBroadcastBean() { } /** * Register the given SSE event sink. * * @param sink the SSE event sink. */ public void register(SseEventSink sink) { synchronized (sse) { if (broadcaster == null) { broadcaster = sse.newBroadcaster(); } } broadcaster.register(sink); } /** * Broadcast the given message 10 times. * * @param message the message. */ public void broadcast(String message) { if (broadcaster != null) { for (int i = 1; i <= 10; i++) { String eventMessage = message + " #" + i; OutboundSseEvent event = sse.newEventBuilder() .data(String.class, eventMessage) .build(); broadcaster.broadcast(event); } } } /** * Close the event sinks. */ public void close() { broadcaster.close(); } } ================================================ FILE: test/coreprofile/integration/src/main/java/cloud/piranha/test/coreprofile/distribution/TRACE.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import jakarta.ws.rs.HttpMethod; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; @Target(METHOD) @Retention(RUNTIME) @HttpMethod("TRACE") public @interface TRACE { } ================================================ FILE: test/coreprofile/integration/src/main/webapp/WEB-INF/beans.xml ================================================ ================================================ FILE: test/coreprofile/integration/src/test/java/cloud/piranha/test/coreprofile/distribution/AsyncIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import static cloud.piranha.test.coreprofile.distribution.ITBase.baseUrl; import jakarta.ws.rs.client.AsyncInvoker; import jakarta.ws.rs.client.Client; import jakarta.ws.rs.client.ClientBuilder; import jakarta.ws.rs.client.WebTarget; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; /** * The integration tests validating async integration works. * * @author Manfred Riem (mriem@manorrock.com) */ public class AsyncIT extends ITBase { /** * Test that validates that an async invocation to an endpoint with the * wrong accept header and using the TRACE HTTP method returns the * NOT_ACCEPTABlE status code. */ @Test void testAsyncNotAcceptable() { Client client = ClientBuilder.newClient(); WebTarget target = client.target(baseUrl + "/async/notAcceptable"); AsyncInvoker invoker = target.request(MediaType.TEXT_XML).async(); Future future = invoker.trace(Response.class); try { Response response = future.get(); assertEquals(406, response.getStatus()); } catch (InterruptedException | ExecutionException ex) { fail(ex); } } } ================================================ FILE: test/coreprofile/integration/src/test/java/cloud/piranha/test/coreprofile/distribution/BeanParamIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class BeanParamIT extends ITBase { @Test public void testBeanParamAnnotationWithoutContent() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + "/beanParam/withoutContent?queryParam=10")) .header("Content-Type", "application/x-www-form-urlencoded") .POST(HttpRequest.BodyPublishers.ofString("formParam=formParam1")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("UserInput{formParam='formParam1', queryParam=10, contentType='application/x-www-form-urlencoded'}", response.body()); } @Test public void testBeanParamAnnotationWithContent() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + "/beanParam/withContent?queryParam=10")) .header("Content-Type", "application/x-www-form-urlencoded") .POST(HttpRequest.BodyPublishers.ofString("CONTENT")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(200, response.statusCode()); assertEquals("CONTENT,UserInput{formParam='null', queryParam=10, contentType='application/x-www-form-urlencoded'}", response.body()); } } ================================================ FILE: test/coreprofile/integration/src/test/java/cloud/piranha/test/coreprofile/distribution/ContainerRequestContextIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import static cloud.piranha.test.coreprofile.distribution.ITBase.baseUrl; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The integration tests validating ContainerRequestContext is available. * * @author Manfred Riem (mriem@manorrock.com) */ public class ContainerRequestContextIT extends ITBase { @Test public void testContainsHeaderString() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(URI.create(baseUrl + "/containerRequestContext/containsHeaderString")) .header("Accept", "text/plain") .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertTrue(response.body().contains("true")); } } ================================================ FILE: test/coreprofile/integration/src/test/java/cloud/piranha/test/coreprofile/distribution/DependencyInjectionIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import org.junit.jupiter.api.Test; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import static org.junit.jupiter.api.Assertions.assertTrue; public class DependencyInjectionIT extends ITBase { @Test public void testDependencyInjection() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(URI.create(baseUrl + "/dependencyInjection")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertTrue(response.body().contains("Dependency Injection works!")); } } ================================================ FILE: test/coreprofile/integration/src/test/java/cloud/piranha/test/coreprofile/distribution/ITBase.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; /** * A base class for all integration tests in this package. * * @author Manfred Riem (mriem@manorrock.com) */ class ITBase { /** * Stores the base URL used by our tests. */ public static String baseUrl = "http://localhost:" + System.getProperty("httpPort", "8080") + "/piranha-test-coreprofile-integration"; } ================================================ FILE: test/coreprofile/integration/src/test/java/cloud/piranha/test/coreprofile/distribution/InterceptorIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * Integration tests to test interceptor integration. * * @author Manfred Riem (mriem@manorrock.com) */ public class InterceptorIT extends ITBase { /** * Test interceptors. * * @throws Exception when a serious error occurs. */ @Test void testInterceptor() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI(baseUrl + "/intercept")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertTrue(response.body().contains("Interceptor works!")); } } ================================================ FILE: test/coreprofile/integration/src/test/java/cloud/piranha/test/coreprofile/distribution/JsonBindingIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The integration tests validating Json Binding integration. * * @author Manfred Riem (mriem@manorrock.com) */ public class JsonBindingIT extends ITBase { /** * Test JSON binding. * * @throws Exception when a serious error occurs. */ @Test void testJsonBinding() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI(baseUrl + "jsonb")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertTrue(response.body().contains("{\"string\":\"JSON Binding works!\"}")); } } ================================================ FILE: test/coreprofile/integration/src/test/java/cloud/piranha/test/coreprofile/distribution/JsonProcessingIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The integration tests validating Json Processing integration. * * @author Manfred Riem (mriem@manorrock.com) */ public class JsonProcessingIT extends ITBase { /** * Test JSON Processing. * * @throws Exception when a serious error occurs. */ @Test void testJsonProcessing() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI(baseUrl + "/jsonp")) .POST(HttpRequest.BodyPublishers.ofString("\"JSON Processing works!\"")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertTrue(response.body().contains("{\"string\":\"JSON Processing works!\"}")); } } ================================================ FILE: test/coreprofile/integration/src/test/java/cloud/piranha/test/coreprofile/distribution/ProducesIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import org.junit.jupiter.api.Test; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; public class ProducesIT extends ITBase { @Test public void notAcceptable() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + "/produces/notAcceptable")) .header("Accept", "text/xml") .GET() .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertEquals(406, response.statusCode()); assertNotEquals("This should not show up!", response.body()); } } ================================================ FILE: test/coreprofile/integration/src/test/java/cloud/piranha/test/coreprofile/distribution/RestIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The integration tests validating REST integration. * * @author Manfred Riem (mriem@manorrock.com) */ public class RestIT extends ITBase { /** * Test REST. * * @throws Exception when a serious error occurs. */ @Test void testREST() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI(baseUrl + "/rest")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertTrue(response.body().contains("REST works!")); } } ================================================ FILE: test/coreprofile/integration/src/test/java/cloud/piranha/test/coreprofile/distribution/SseIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.distribution; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * Integration tests to test SSE integration. * * @author Manfred Riem (mriem@manorrock.com) */ public class SseIT extends ITBase { /** * Test string based SSE. * * @throws Exception when a serious error occurs. */ @Test void testSseString() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI(baseUrl + "/sse/string")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); assertNotNull(response.body()); assertTrue(response.body().contains("data: Event 4")); } /** * Test SSE broadcast. * * @throw Exception when a serious error occurs. */ @Test void testSseBroadcast() throws Exception { List events = new ArrayList<>(); HttpClient client = HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(120)) .build(); /* * Register client and collect events. */ CompletableFuture future = client.sendAsync(HttpRequest.newBuilder() .uri(new URI(baseUrl + "/sse/register")) .build(), BodyHandlers.ofLines()) .thenAccept(response -> response.body().forEach(event -> { if (!event.isBlank()) { events.add(event); } })); /* * Give client time to bootstrap. */ Thread.sleep(2000); /* * Simulate server broadcast. */ client.send(HttpRequest.newBuilder() .uri(new URI(baseUrl + "/sse/broadcast")) .POST(HttpRequest.BodyPublishers.ofString("Broadcast message")) .build(), HttpResponse.BodyHandlers.ofString()); /* * Wait for the future to complete. */ try { future.get(10, TimeUnit.SECONDS); } catch (Exception e) { } /* * Check if we have received 10 events. */ assertEquals(10, events.size(), "Should have received 10 events"); /* * Close broadcast. */ HttpResponse closeResponse = client.send(HttpRequest.newBuilder() .uri(new URI(baseUrl + "/sse/close")) .timeout(Duration.ofMinutes(5)) .GET() .build(), HttpResponse.BodyHandlers.discarding()); /* * Give the server time to close. */ Thread.sleep(2000); assertEquals(204, closeResponse.statusCode()); } } ================================================ FILE: test/coreprofile/json/pom.xml ================================================ 4.0.0 cloud.piranha.test.coreprofile project 25.4.0-SNAPSHOT piranha-test-coreprofile-json war Piranha - Test - Core Profile - Create a JSON temperature service UTF-8 jakarta.platform jakarta.jakartaee-core-api provided org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test temperature cloud.piranha.maven piranha-maven-plugin ${project.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop ${httpPort} org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify 1 ${httpPort} org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false org.codehaus.mojo build-helper-maven-plugin reserve-network-port reserve-network-port package httpPort ================================================ FILE: test/coreprofile/json/src/main/java/temperature/Temperature.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package temperature; public class Temperature { /** * Defines the temperature scale enum. */ public enum TemperatureScale { /** * Celsius constant. */ CELSIUS, /** * Fahrenheit constant. */ FAHRENHEIT } /** * Stores the temperature scale. */ private TemperatureScale scale; /** * Stores the temperature. */ private double temperature; /** * Get the temperature scale. * * @return the temperature scale. */ public TemperatureScale getScale() { return scale; } /** * Get the temperature. * * @return the temperature. */ public double getTemperature() { return temperature; } /** * Set the temperature scale. * * @param scale the temperature scale. */ public void setScale(TemperatureScale scale) { this.scale = scale; } /** * Set the temperature. * * @param temperature the temperature. */ public void setTemperature(double temperature) { this.temperature = temperature; } } ================================================ FILE: test/coreprofile/json/src/main/java/temperature/TemperatureApplication.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package temperature; import jakarta.ws.rs.ApplicationPath; import jakarta.ws.rs.core.Application; @ApplicationPath("") public class TemperatureApplication extends Application { } ================================================ FILE: test/coreprofile/json/src/main/java/temperature/TemperatureBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package temperature; import jakarta.enterprise.context.RequestScoped; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; import static temperature.Temperature.TemperatureScale.CELSIUS; import static temperature.Temperature.TemperatureScale.FAHRENHEIT; @RequestScoped @Path("") public class TemperatureBean { /** * Get the temperature in celsius. * * @param celsius the celsius temperature. * @return the celsius tempature. */ @GET @Produces(APPLICATION_JSON) @Path("/celsius/{celsius}") public Temperature celsius(@PathParam("celsius") double celsius) { Temperature temp = new Temperature(); temp.setScale(CELSIUS); temp.setTemperature(celsius); return temp; } /** * Get the temperature in fahrenheit. * * @param fahrenheit the fahrenheit temperature. * @return the fahrenheit tempature. */ @GET @Produces("application/json") @Path("/fahrenheit/{fahrenheit}") public Temperature fahrenheit(@PathParam("fahrenheit") double fahrenheit) { Temperature temp = new Temperature(); temp.setScale(FAHRENHEIT); temp.setTemperature(fahrenheit); return temp; } } ================================================ FILE: test/coreprofile/json/src/test/java/temperature/TemperatureIT.java ================================================ package temperature; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; class TemperatureIT { private String httpPort = System.getProperty("httpPort"); @Test void testCelsius() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpPort + "/temperature/celsius/18.5")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertEquals("{\"scale\":\"CELSIUS\",\"temperature\":18.5}", response.body()); } @Test void testFahrenheit() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpPort + "/temperature/fahrenheit/68.0")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertEquals("{\"scale\":\"FAHRENHEIT\",\"temperature\":68.0}", response.body()); } } ================================================ FILE: test/coreprofile/no_servlet_class/pom.xml ================================================ 4.0.0 cloud.piranha.test.coreprofile project 25.4.0-SNAPSHOT piranha-test-coreprofile-no_servlet_class war Piranha - Test - Core Profile - No servlet-class specified UTF-8 jakarta.platform jakarta.jakartaee-core-api provided org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test piranha-test-coreprofile-no_servlet_class cloud.piranha.maven piranha-maven-plugin ${project.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop ${httpPort} org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify 1 ${httpPort} org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false org.codehaus.mojo build-helper-maven-plugin reserve-network-port reserve-network-port package httpPort debug cloud.piranha.maven piranha-maven-plugin ${project.version} -Xdebug -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=9009 ================================================ FILE: test/coreprofile/no_servlet_class/src/main/java/cloud/piranha/test/coreprofile/no_servlet_class/EchoApplication.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.no_servlet_class; import jakarta.ws.rs.core.Application; import java.util.HashSet; import java.util.Set; public class EchoApplication extends Application { @Override public Set> getClasses() { Set> classes = new HashSet<>(); classes.add(EchoBean.class); return classes; } } ================================================ FILE: test/coreprofile/no_servlet_class/src/main/java/cloud/piranha/test/coreprofile/no_servlet_class/EchoBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.no_servlet_class; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; @Path("") public class EchoBean { /** * Get the 'echo' string. * * @return 'echo' */ @GET @Produces(TEXT_PLAIN) @Path("/echo") public String echo() { return "echo"; } } ================================================ FILE: test/coreprofile/no_servlet_class/src/main/webapp/WEB-INF/web.xml ================================================ cloud.piranha.test.coreprofile.no_servlet_class.EchoApplication cloud.piranha.test.coreprofile.no_servlet_class.EchoApplication /* 30 ================================================ FILE: test/coreprofile/no_servlet_class/src/test/java/cloud/piranha/test/coreprofile/no_servlet_class/EchoIT.java ================================================ package cloud.piranha.test.coreprofile.no_servlet_class; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; class EchoIT { private String httpPort = System.getProperty("httpPort"); @Test void testEcho() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpPort + "/piranha-test-coreprofile-no_servlet_class/echo")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertEquals("echo", response.body()); } } ================================================ FILE: test/coreprofile/pom.xml ================================================ 4.0.0 cloud.piranha.test project 25.4.0-SNAPSHOT cloud.piranha.test.coreprofile project pom Piranha - Test - Core Profile - Project cloud.piranha.dist piranha-dist-coreprofile ${project.version} test arquillian integration json no_servlet_class rest ================================================ FILE: test/coreprofile/rest/pom.xml ================================================ 4.0.0 cloud.piranha.test.coreprofile project 25.4.0-SNAPSHOT piranha-test-coreprofile-rest war Piranha - Test - Core Profile - REST UTF-8 jakarta.ws.rs jakarta.ws.rs-api provided org.junit.jupiter junit-jupiter-api test rest cloud.piranha.maven piranha-maven-plugin ${project.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop /rest ${httpPort} org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify 1 ${httpPort} org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false org.codehaus.mojo build-helper-maven-plugin reserve-network-port reserve-network-port package httpPort ================================================ FILE: test/coreprofile/rest/src/main/java/cloud/piranha/test/coreprofile/rest/RestApplication.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.rest; import jakarta.ws.rs.ApplicationPath; import jakarta.ws.rs.core.Application; /** * A REST application. * * @author Manfred Riem (mriem@manorrock.com) */ @ApplicationPath("") public class RestApplication extends Application { } ================================================ FILE: test/coreprofile/rest/src/main/java/cloud/piranha/test/coreprofile/rest/RestBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.rest; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; /** * A REST bean. * * @author Manfred Riem (mriem@manorrock.com) */ @Path("") public class RestBean { /** * Invoke the '/hellorest' endpoint. * * @return "Hello REST" */ @GET @Produces(TEXT_PLAIN) @Path("/hellorest") public String helloRest() { return "Hello REST"; } } ================================================ FILE: test/coreprofile/rest/src/main/webapp/WEB-INF/web.xml ================================================ 30 ================================================ FILE: test/coreprofile/rest/src/test/java/cloud/piranha/test/coreprofile/rest/RestIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.coreprofile.rest; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; class RestIT { private String httpPort = System.getProperty("httpPort"); @Test void testHelloRest() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + httpPort + "/rest/hellorest")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertEquals("Hello REST", response.body()); } } ================================================ FILE: test/embedded/classloader/pom.xml ================================================ 4.0.0 cloud.piranha.test.embedded project 25.4.0-SNAPSHOT piranha-test-embedded-classloader war Piranha - Test - Embedded - Classloader UTF-8 jakarta.platform jakarta.jakartaee-web-api provided org.apache.maven.plugins maven-war-plugin false ================================================ FILE: test/embedded/classloader/src/main/java/classloader/ClassLoaderServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package classloader; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; /** * The ClassLoaderServlet Servlet. * * @author Manfred Riem (mriem@manorrock.com) */ @WebServlet(name = "ClassLoaderServlet", urlPatterns = {"/ClassLoaderServlet"}) public class ClassLoaderServlet extends HttpServlet { /** * Stores the serial version UID. */ private static final long serialVersionUID = 5841417173572330850L; } ================================================ FILE: test/embedded/classloader/src/main/webapp/META-INF/context.xml ================================================ ================================================ FILE: test/embedded/classloader2/pom.xml ================================================ 4.0.0 cloud.piranha.test.embedded project 25.4.0-SNAPSHOT piranha-test-embedded-classloader2 jar Piranha - Test - Embedded - Classloader #2 UTF-8 jakarta.platform jakarta.jakartaee-web-api provided cloud.piranha.core piranha-core-impl ${project.version} test cloud.piranha.resource piranha-resource-impl ${project.version} test cloud.piranha.test.embedded piranha-test-embedded-classloader ${project.version} war test org.junit.jupiter junit-jupiter-api test org.apache.maven.plugins maven-install-plugin default-install none org.apache.maven.plugins maven-jar-plugin default-jar none org.apache.maven.plugins maven-dependency-plugin unpack pre-integration-test unpack cloud.piranha.test.embedded piranha-test-embedded-classloader ${project.version} war true ${project.build.directory}/classloader ================================================ FILE: test/embedded/classloader2/src/test/java/classloader/ClassLoaderIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package classloader; import cloud.piranha.core.impl.DefaultWebApplicationClassLoader; import cloud.piranha.resource.impl.DefaultResourceManager; import cloud.piranha.resource.impl.DirectoryResource; import org.junit.jupiter.api.Test; import java.io.File; import static org.junit.jupiter.api.Assertions.assertNotNull; /** * The integration tests for the DefaultWebApplicationClassLoader. * * @author Manfred Riem (mriem@manorrock.com) */ class ClassLoaderIT { /** * Test loadClass method. * * @throws Exception when a serious error occurs. */ @Test void testLoadClass() throws Exception { DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(); DefaultResourceManager resourceManager = new DefaultResourceManager(); resourceManager.addResource(new DirectoryResource(new File("target/classloader/WEB-INF/classes"))); classLoader.setResourceManager(resourceManager); assertNotNull(classLoader.loadClass("classloader.ClassLoaderServlet", true)); } /** * Test loadClass method. * * @throws Exception when a serious error occurs. */ @Test void testLoadClass2() throws Exception { DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(); DefaultResourceManager resourceManager = new DefaultResourceManager(); resourceManager.addResource(new DirectoryResource(new File("target/classloader/WEB-INF/classes"))); classLoader.setResourceManager(resourceManager); assertNotNull(classLoader.loadClass("classloader.ClassLoaderServlet", true)); assertNotNull(classLoader.loadClass("classloader.ClassLoaderServlet", true)); } } ================================================ FILE: test/embedded/eclipselink/pom.xml ================================================ 4.0.0 cloud.piranha.test.embedded project 25.4.0-SNAPSHOT piranha-test-embedded-eclipselink war Piranha - Test - Embedded - EclipseLink UTF-8 jakarta.persistence jakarta.persistence-api compile jakarta.enterprise jakarta.enterprise.cdi-api provided cloud.piranha piranha-embedded ${project.version} test cloud.piranha.core piranha-core-impl ${project.version} test cloud.piranha.extension piranha-extension-expressly ${project.version} test cloud.piranha.extension piranha-extension-eclipselink ${project.version} test cloud.piranha.extension piranha-extension-mojarra ${project.version} test cloud.piranha.extension piranha-extension-wasp ${project.version} test cloud.piranha.extension piranha-extension-yasson ${project.version} test cloud.piranha.extension piranha-extension-herring ${project.version} test cloud.piranha.extension piranha-extension-scinitializer ${project.version} test cloud.piranha.extension piranha-extension-webxml ${project.version} test cloud.piranha.extension piranha-extension-weld ${project.version} test com.h2database h2 test jakarta.websocket jakarta.websocket-api test jakarta.websocket jakarta.websocket-client-api test org.junit.jupiter junit-jupiter-api test org.junit.platform junit-platform-launcher test ================================================ FILE: test/embedded/eclipselink/src/main/java/eclipselink/EclipseLinkBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package eclipselink; import java.util.List; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Named; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.Persistence; import jakarta.persistence.TypedQuery; /** * A simple bean. * * @author Manfred Riem (mriem@manorrock.com) */ @Named(value = "eclipseLinkBean") @RequestScoped public class EclipseLinkBean { /** * Stores the entity manager factory. */ private static EntityManagerFactory emf; /** * {@return the entity manager} */ public EntityManager getEntityManager() { synchronized(this) { if (emf == null) { emf = Persistence.createEntityManagerFactory("demo"); } } return emf.createEntityManager(); } /** * {@return the count message} */ public String getMessage() { TypedQuery query = getEntityManager().createQuery("SELECT o FROM EclipseLinkTable AS o", EclipseLinkTable.class); List list = query.getResultList(); return "Count: " + list.size(); } } ================================================ FILE: test/embedded/eclipselink/src/main/java/eclipselink/EclipseLinkTable.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package eclipselink; import java.io.Serializable; import jakarta.persistence.Entity; import jakarta.persistence.Id; /** * An EclipseLink table. * * @author Manfred Riem (mriem@manorrock.com) */ @Entity public class EclipseLinkTable implements Serializable { /** * Stores the id (primary key). */ @Id private Long id; /** * {@return the id} */ public Long getId() { return id; } /** * Set the id. * * @param id the id. */ public void setId(Long id) { this.id = id; } } ================================================ FILE: test/embedded/eclipselink/src/main/resources/META-INF/persistence.xml ================================================ org.eclipse.persistence.jpa.PersistenceProvider jdbc/demo eclipselink.EclipseLinkTable false ================================================ FILE: test/embedded/eclipselink/src/main/webapp/WEB-INF/beans.xml ================================================ ================================================ FILE: test/embedded/eclipselink/src/main/webapp/WEB-INF/faces-config.xml ================================================ ================================================ FILE: test/embedded/eclipselink/src/main/webapp/WEB-INF/web.xml ================================================ Faces Servlet jakarta.faces.webapp.FacesServlet 1 Faces Servlet *.html 30 jdbc/demo org.h2.jdbcx.JdbcDataSource jdbc:h2:mem:eclipselink;DB_CLOSE_DELAY=-1 sa ================================================ FILE: test/embedded/eclipselink/src/main/webapp/index.xhtml ================================================ ================================================ FILE: test/embedded/eclipselink/src/test/java/eclipselink/EclipseLinkTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package eclipselink; import cloud.piranha.embedded.EmbeddedPiranha; import cloud.piranha.embedded.EmbeddedPiranhaBuilder; import cloud.piranha.embedded.EmbeddedRequest; import cloud.piranha.embedded.EmbeddedRequestBuilder; import cloud.piranha.embedded.EmbeddedResponse; import cloud.piranha.extension.herring.HerringExtension; import cloud.piranha.extension.scinitializer.ServletContainerInitializerExtension; import cloud.piranha.extension.webxml.WebXmlExtension; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the Hello Weld web application. * * @author Manfred Riem (mriem@manorrock.com) */ class EclipseLinkTest { /** * Test /index.html. * * @throws Exception */ @Test void testIndexHtml() throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .directoryResource("src/main/webapp") .aliasedDirectoryResource("target/classes", "/WEB-INF/classes") .extension(HerringExtension.class) .extension(WebXmlExtension.class) .extension(ServletContainerInitializerExtension.class) .build() .start(); EmbeddedRequest request = new EmbeddedRequestBuilder() .contextPath("") .servletPath("/index.html") .build(); EmbeddedResponse response = new EmbeddedResponse(); piranha.service(request, response); assertEquals(200, response.getStatus()); assertTrue(response.getResponseAsString().contains("Count: 0")); piranha.stop().destroy(); } } ================================================ FILE: test/embedded/exousia/pom.xml ================================================ 4.0.0 cloud.piranha.test.embedded project 25.4.0-SNAPSHOT piranha-test-embedded-exousia war Piranha - Test - Embedded - Exousia UTF-8 cloud.piranha.extension piranha-extension-security-servlet ${project.version} test cloud.piranha.extension piranha-extension-webxml ${project.version} test cloud.piranha piranha-embedded ${project.version} test org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test ================================================ FILE: test/embedded/exousia/src/test/java/exousia/ExousiaTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package exousia; import cloud.piranha.embedded.EmbeddedPiranha; import cloud.piranha.embedded.EmbeddedPiranhaBuilder; import cloud.piranha.embedded.EmbeddedRequest; import cloud.piranha.embedded.EmbeddedRequestBuilder; import cloud.piranha.embedded.EmbeddedResponse; import cloud.piranha.extension.exousia.AuthorizationPostInitializer; import cloud.piranha.extension.exousia.AuthorizationPreInitializer; import cloud.piranha.extension.webxml.WebXmlInitializer; import static cloud.piranha.extension.exousia.AuthorizationPreInitializer.AUTHZ_FACTORY_CLASS; import static cloud.piranha.extension.exousia.AuthorizationPreInitializer.AUTHZ_POLICY_CLASS; import static cloud.piranha.extension.exousia.AuthorizationPreInitializer.UNCHECKED_PERMISSIONS; import cloud.piranha.extension.security.servlet.ServletSecurityManagerInitializer; import static java.util.Arrays.asList; import jakarta.security.jacc.WebUserDataPermission; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; import org.glassfish.exousia.modules.def.DefaultPolicy; import org.glassfish.exousia.modules.def.DefaultPolicyConfigurationFactory; /** * The JUnit tests for the basic connection test * * @author Arjan Tijms (arjan.tijms@gmail.com) */ class ExousiaTest { /** * Test basic connection permission using a non-secure (http) connection. * * @throws Exception */ @Test void testNonSecureConnection() throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .initializer(ServletSecurityManagerInitializer.class) .initializer(WebXmlInitializer.class) .attribute(AUTHZ_FACTORY_CLASS, DefaultPolicyConfigurationFactory.class) .attribute(AUTHZ_POLICY_CLASS, DefaultPolicy.class) .attribute(UNCHECKED_PERMISSIONS, asList( new WebUserDataPermission("/*", "!GET"), new WebUserDataPermission("/*", "GET:CONFIDENTIAL"))) .initializer(AuthorizationPreInitializer.class) .initializer(AuthorizationPostInitializer.class) .servlet("PublicServlet", PublicServlet.class) .servletMapping("PublicServlet", "/public/servlet") .build() .start(); EmbeddedRequest request = new EmbeddedRequestBuilder() .contextPath("") .servletPath("/public/servlet") .build(); EmbeddedResponse response = new EmbeddedResponse(); piranha.service(request, response); assertFalse(response.getResponseAsString().contains("Hello, from Servlet!")); piranha.stop().destroy(); } /** * Test basic connection permission using a secure (https) connection. * * @throws Exception */ @Test void testSecureConnection() throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .initializer(ServletSecurityManagerInitializer.class.getName()) .initializer(WebXmlInitializer.class.getName()) .attribute(AUTHZ_FACTORY_CLASS, DefaultPolicyConfigurationFactory.class) .attribute(AUTHZ_POLICY_CLASS, DefaultPolicy.class) .attribute(UNCHECKED_PERMISSIONS, asList( new WebUserDataPermission("/*", "!GET"), new WebUserDataPermission("/*", "GET:CONFIDENTIAL"))) .initializer(AuthorizationPreInitializer.class) .initializer(AuthorizationPostInitializer.class) .servlet("PublicServlet", PublicServlet.class) .servletMapping("PublicServlet", "/public/servlet") .build() .start(); EmbeddedRequest request = new EmbeddedRequestBuilder() .contextPath("") .servletPath("/public/servlet") .scheme("https") .build(); EmbeddedResponse response = new EmbeddedResponse(); piranha.service(request, response); assertEquals(200, response.getStatus()); assertTrue(response.getResponseAsString().contains("Hello, from Servlet!")); piranha.stop().destroy(); } @Test void testSecureConnectionExactMapping() throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .initializer(ServletSecurityManagerInitializer.class.getName()) .initializer(WebXmlInitializer.class.getName()) .attribute(AUTHZ_FACTORY_CLASS, DefaultPolicyConfigurationFactory.class) .attribute(AUTHZ_POLICY_CLASS, DefaultPolicy.class) .attribute(UNCHECKED_PERMISSIONS, asList( new WebUserDataPermission("/public/servlet", "!GET"), new WebUserDataPermission("/public/servlet", "GET:CONFIDENTIAL"))) .initializer(AuthorizationPreInitializer.class) .initializer(AuthorizationPostInitializer.class) .servlet("PublicServlet", PublicServlet.class) .servletMapping("PublicServlet", "/public/servlet") .build() .start(); EmbeddedRequest request = new EmbeddedRequestBuilder() .contextPath("") .servletPath("/public/servlet") .scheme("https") .build(); EmbeddedResponse response = new EmbeddedResponse(); piranha.service(request, response); assertEquals(200, response.getStatus()); assertTrue(response.getResponseAsString().contains("Hello, from Servlet!")); piranha.stop().destroy(); } } ================================================ FILE: test/embedded/exousia/src/test/java/exousia/PublicServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package exousia; import java.io.IOException; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * This Servlet writes out a text * * @author Arjan Tijms * */ @WebServlet(urlPatterns = "/public/servlet") public class PublicServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("Hello, from Servlet!"); } } ================================================ FILE: test/embedded/hazelcast/pom.xml ================================================ 4.0.0 cloud.piranha.test.embedded project 25.4.0-SNAPSHOT piranha-test-embedded-hazelcast war Piranha - Test - Embedded - Hazelcast HTTP Session UTF-8 jakarta.enterprise jakarta.enterprise.cdi-api provided cloud.piranha piranha-embedded ${project.version} test cloud.piranha.extension piranha-extension-expressly ${project.version} test cloud.piranha.extension piranha-extension-jstl ${project.version} test cloud.piranha.extension piranha-extension-mojarra ${project.version} test cloud.piranha.extension piranha-extension-wasp ${project.version} test cloud.piranha.extension piranha-extension-hazelcast ${project.version} test cloud.piranha.extension piranha-extension-herring ${project.version} test cloud.piranha.extension piranha-extension-scinitializer ${project.version} test cloud.piranha.extension piranha-extension-webxml ${project.version} test cloud.piranha.extension piranha-extension-weld ${project.version} test jakarta.websocket jakarta.websocket-api test jakarta.websocket jakarta.websocket-client-api test org.junit.jupiter junit-jupiter-api test ================================================ FILE: test/embedded/hazelcast/src/main/java/hazelcast/HazelcastBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package hazelcast; import jakarta.enterprise.context.RequestScoped; /** * The Hazelcast bean. * * @author Manfred Riem (mriem@manorrock.com) */ @RequestScoped public class HazelcastBean { } ================================================ FILE: test/embedded/hazelcast/src/main/webapp/WEB-INF/beans.xml ================================================ ================================================ FILE: test/embedded/hazelcast/src/main/webapp/WEB-INF/web.xml ================================================ Faces Servlet jakarta.faces.webapp.FacesServlet 1 Faces Servlet *.html ================================================ FILE: test/embedded/hazelcast/src/main/webapp/index.xhtml ================================================ Hello Hazelcast Hello Hazelcast, your session id is: ================================================ FILE: test/embedded/hazelcast/src/test/java/hazelcast/HazelcastTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package hazelcast; import cloud.piranha.embedded.EmbeddedPiranha; import cloud.piranha.embedded.EmbeddedPiranhaBuilder; import cloud.piranha.embedded.EmbeddedRequest; import cloud.piranha.embedded.EmbeddedRequestBuilder; import cloud.piranha.embedded.EmbeddedResponse; import cloud.piranha.extension.hazelcast.HazelcastHttpSessionManager; import cloud.piranha.extension.herring.HerringExtension; import cloud.piranha.extension.scinitializer.ServletContainerInitializerExtension; import cloud.piranha.extension.webxml.WebXmlExtension; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The JUnit tests for the Hazelcast web application. * * @author Manfred Riem (mriem@manorrock.com) */ class HazelcastTest { /** * Test /faces/notfound.html. * * @throws Exception */ @Test void testNotFound() throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .directoryResource("src/main/webapp") .aliasedDirectoryResource("target/classes", "/WEB-INF/classes") .httpSessionManager(new HazelcastHttpSessionManager()) .extension(HerringExtension.class) .extension(WebXmlExtension.class) .extension(ServletContainerInitializerExtension.class) .build() .start(); EmbeddedRequest request = new EmbeddedRequestBuilder() .contextPath("") .servletPath("/notfound.nf") .build(); EmbeddedResponse response = new EmbeddedResponse(); piranha.service(request, response); assertEquals(404, response.getStatus()); piranha.stop() .destroy(); } /** * Test /index.html. * * @throws Exception */ @Test void testIndexHtml() throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .directoryResource("src/main/webapp") .aliasedDirectoryResource("target/classes", "/WEB-INF/classes") .httpSessionManager(new HazelcastHttpSessionManager()) .extension(HerringExtension.class) .extension(WebXmlExtension.class) .extension(ServletContainerInitializerExtension.class) .build() .start(); EmbeddedRequest request = new EmbeddedRequestBuilder() .contextPath("") .servletPath("/index.html") .build(); request.setWebApplication(piranha.getWebApplication()); EmbeddedResponse response = new EmbeddedResponse(); response.setWebApplication(piranha.getWebApplication()); piranha.service(request, response); assertEquals(200, response.getStatus()); assertTrue(response.getResponseAsString().contains("Hello Hazelcast")); piranha.stop() .destroy(); } } ================================================ FILE: test/embedded/helloworld/pom.xml ================================================ 4.0.0 cloud.piranha.test.embedded project 25.4.0-SNAPSHOT piranha-test-embedded-helloworld war Piranha - Test - Embedded - HelloWorld UTF-8 cloud.piranha piranha-embedded ${project.version} compile cloud.piranha.http piranha-http-impl ${project.version} compile cloud.piranha.http piranha-http-webapp ${project.version} compile cloud.piranha.extension piranha-extension-servlet ${project.version} compile org.junit.jupiter junit-jupiter test piranha-test-embedded-helloworld org.apache.maven.plugins maven-war-plugin false ================================================ FILE: test/embedded/helloworld/src/main/java/helloworld/HelloWorldApplication.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package helloworld; import cloud.piranha.embedded.EmbeddedPiranha; import cloud.piranha.embedded.EmbeddedPiranhaBuilder; import cloud.piranha.http.impl.DefaultHttpServer; import cloud.piranha.http.webapp.HttpWebApplicationServer; public class HelloWorldApplication { /** * Main method. * * @param arguments the arguments. * @throws Exception when an error occurs. */ public static void main(String[] arguments) throws Exception { EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .servlet("HelloWorld", HelloWorldServlet.class) .servletMapping("HelloWorld", "/*") .buildAndStart(); HttpWebApplicationServer webAppServer = new HttpWebApplicationServer(); webAppServer.addWebApplication(piranha.getWebApplication()); DefaultHttpServer server = new DefaultHttpServer(8080, webAppServer, false); server.start(); } } ================================================ FILE: test/embedded/helloworld/src/main/java/helloworld/HelloWorldServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package helloworld; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * A 'Hello World!' Servlet. * * @author Manfred Riem (mriem@manorrock.com) */ public class HelloWorldServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); try ( PrintWriter out = response.getWriter()) { out.println(""" Hello World!

Hello World!

"""); } } } ================================================ FILE: test/embedded/helloworld/src/main/webapp/WEB-INF/web.xml ================================================ HelloWorldServlet helloworld.HelloWorldServlet HelloWorldServlet /index.html 30 ================================================ FILE: test/embedded/helloworld/src/test/java/helloworld/HelloWorldTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package helloworld; import cloud.piranha.embedded.EmbeddedPiranha; import cloud.piranha.embedded.EmbeddedPiranhaBuilder; import cloud.piranha.embedded.EmbeddedRequest; import cloud.piranha.embedded.EmbeddedRequestBuilder; import cloud.piranha.embedded.EmbeddedResponse; import cloud.piranha.embedded.EmbeddedResponseBuilder; import cloud.piranha.extension.servlet.ServletExtension; import org.junit.jupiter.api.AfterEach; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * The 'Hello World!' test. * * @author Manfred Riem (mriem@manorrock.com) */ class HelloWorldTest { /** * Stores the Piranha instance. */ private EmbeddedPiranha piranha; /** * After each. */ @AfterEach void afterEach() { piranha.stop(); } /** * Before each. */ @BeforeEach void beforeEach() { piranha = new EmbeddedPiranhaBuilder() .directoryResource("src/main/webapp") .extension(ServletExtension.class) .buildAndStart(); } /** * Test the 'Hello World!' servlet. * * @throws Exception when a serious error occurs. */ @Test void testHelloWorld() throws Exception { EmbeddedRequest request = new EmbeddedRequestBuilder() .build(); EmbeddedResponse response = new EmbeddedResponseBuilder() .bodyOnly(true) .build(); piranha.service(request, response); assertTrue(response.getResponseAsString().contains("Hello World!")); } } ================================================ FILE: test/embedded/pom.xml ================================================ 4.0.0 cloud.piranha.test project 25.4.0-SNAPSHOT cloud.piranha.test.embedded project pom Piranha - Test - Embedded - Project classloader classloader2 eclipselink exousia hazelcast helloworld soteria-basic soteria-form springboot springboot-virtualthreads web-fragment-in-jar web-fragment-in-jar-test web-fragment-in-jar-webapp weld ================================================ FILE: test/embedded/soteria-basic/pom.xml ================================================ 4.0.0 cloud.piranha.test.embedded project 25.4.0-SNAPSHOT piranha-test-embedded-soteria-basic war Piranha - Test - Embedded - Soteria BASIC jakarta.platform jakarta.jakartaee-web-api provided cloud.piranha piranha-embedded ${project.version} test cloud.piranha.extension piranha-extension-exousia ${project.version} test cloud.piranha.extension piranha-extension-herring ${project.version} test cloud.piranha.extension piranha-extension-security-jakarta ${project.version} test cloud.piranha.extension piranha-extension-webxml ${project.version} test cloud.piranha.extension piranha-extension-weld ${project.version} test org.junit.jupiter junit-jupiter-api test ================================================ FILE: test/embedded/soteria-basic/src/test/java/basic/BasicTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package basic; import static cloud.piranha.extension.exousia.AuthorizationPreInitializer.CONSTRAINTS; import static java.util.Arrays.asList; import static javax.naming.Context.INITIAL_CONTEXT_FACTORY; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Base64; import org.glassfish.exousia.constraints.SecurityConstraint; import org.junit.jupiter.api.Test; import cloud.piranha.embedded.EmbeddedPiranha; import cloud.piranha.embedded.EmbeddedPiranhaBuilder; import cloud.piranha.embedded.EmbeddedRequest; import cloud.piranha.embedded.EmbeddedRequestBuilder; import cloud.piranha.embedded.EmbeddedResponse; import cloud.piranha.extension.exousia.AuthorizationPostInitializer; import cloud.piranha.extension.herring.HerringExtension; import cloud.piranha.extension.security.jakarta.JakartaSecurityAllInitializer; import cloud.piranha.extension.security.servlet.ServletSecurityManagerExtension; import cloud.piranha.extension.webxml.WebXmlInitializer; import jakarta.security.enterprise.authentication.mechanism.http.BasicAuthenticationMechanismDefinition; import org.junit.jupiter.api.Disabled; @BasicAuthenticationMechanismDefinition(realmName = "test") @Disabled class BasicTest { @Test void testAuthenticated() throws Exception { System.getProperties().put(INITIAL_CONTEXT_FACTORY, DynamicInitialContextFactory.class.getName()); EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .attribute(CONSTRAINTS, asList( new SecurityConstraint("/protected/servlet", "architect"))) .extensions( ServletSecurityManagerExtension.class, HerringExtension.class) .initializers( WebXmlInitializer.class, JakartaSecurityAllInitializer.class, AuthorizationPostInitializer.class) .servletsMapped( PublicServlet.class, "/public/servlet", ProtectedServlet.class, "/protected/servlet") .buildAndStart(); EmbeddedRequest request = new EmbeddedRequestBuilder() .servletPath("/protected/servlet") .header("Authorization", "Basic " + Base64.getEncoder(). encodeToString("test:test".getBytes())) .parameter("doLogin", "true") .build(); EmbeddedResponse response = piranha.service(request); // Now has to be logged-in so page is accessible assertTrue( response.getResponseAsString().contains("This is a protected servlet"), "Should have been authenticated, but could not access protected resource" ); // Not only does the page needs to be accessible, the caller should have // the correct // name and roles as well // Being able to access a page protected by a role but then seeing the un-authenticated // (anonymous) user would normally be impossible, but could happen if the authorization // system checks roles on the authenticated subject, but does not correctly expose // or propagate these to the HttpServletRequest assertFalse( response.getResponseAsString().contains("web username: null"), "Protected resource could be accessed, but the user appears to be the unauthenticated user. " + "This should not be possible" ); // An authenticated user should have the exact name "test" and nothing else. assertTrue( response.getResponseAsString().contains("web username: test"), "Protected resource could be accessed, but the username is not correct." ); // Being able to access a page protected by role "architect" but failing // the test for this role would normally be impossible, but could happen if the // authorization system checks roles on the authenticated subject, but does not // correctly expose or propagate these to the HttpServletRequest assertTrue( response.getResponseAsString().contains("web user has role \"architect\": true"), "Resource protected by role \"architect\" could be accessed, but user fails test for this role." + "This should not be possible" ); piranha.stop().destroy(); } } ================================================ FILE: test/embedded/soteria-basic/src/test/java/basic/DynamicInitialContextFactory.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package basic; import java.lang.reflect.Field; import java.util.Hashtable; import javax.naming.Context; import javax.naming.NamingException; import javax.naming.spi.InitialContextFactory; import com.manorrock.herring.DefaultInitialContext; /** * The default InitialContextFactory. * * @author Manfred Riem (mriem@manorrock.com) * @author Arjan Tijms */ public class DynamicInitialContextFactory implements InitialContextFactory { /** * Stores the initial context. */ private static final DefaultInitialContext INITIAL_CONTEXT = new DefaultInitialContext(); /** * {@return the initial context} * @param environment the environment. * @throws NamingException when a naming error occurs. */ @Override public Context getInitialContext(Hashtable environment) throws NamingException { try { Field closedField = DefaultInitialContext.class.getDeclaredField("closed"); closedField.setAccessible(true); closedField.setBoolean(INITIAL_CONTEXT, false); } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); } return INITIAL_CONTEXT; } } ================================================ FILE: test/embedded/soteria-basic/src/test/java/basic/ProtectedServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package basic; import java.io.IOException; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * * @author Arjan Tijms * */ @WebServlet(urlPatterns = "/protected/servlet") public class ProtectedServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("This is a protected servlet \n"); String webName = null; if (request.getUserPrincipal() != null) { webName = request.getUserPrincipal().getName(); } response.getWriter().write("web username: " + webName + "\n"); boolean webHasRole = request.isUserInRole("architect"); response.getWriter().write("web user has role \"architect\": " + webHasRole + "\n"); } } ================================================ FILE: test/embedded/soteria-basic/src/test/java/basic/PublicServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package basic; import java.io.IOException; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * This Servlet writes out the current caller principal name and checks whether the * caller is in the "architect" role. * * @author Arjan Tijms * */ @WebServlet(urlPatterns = "/public/servlet") public class PublicServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("This is a public servlet \n"); String webName = null; if (request.getUserPrincipal() != null) { webName = request.getUserPrincipal().getName(); } response.getWriter().write("web username: " + webName + "\n"); boolean webHasRole = request.isUserInRole("architect"); response.getWriter().write("web user has role \"architect\": " + webHasRole + "\n"); } } ================================================ FILE: test/embedded/soteria-basic/src/test/java/basic/TestIdentityStore.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package basic; import static java.util.Arrays.asList; import static jakarta.security.enterprise.identitystore.CredentialValidationResult.INVALID_RESULT; import java.util.HashSet; import jakarta.enterprise.context.ApplicationScoped; import jakarta.security.enterprise.credential.UsernamePasswordCredential; import jakarta.security.enterprise.identitystore.CredentialValidationResult; import jakarta.security.enterprise.identitystore.IdentityStore; /** * * @author Arjan Tijms * */ @ApplicationScoped public class TestIdentityStore implements IdentityStore { public CredentialValidationResult validate(UsernamePasswordCredential usernamePasswordCredential) { if (usernamePasswordCredential.compareTo("test", "test")) { return new CredentialValidationResult("test", new HashSet<>(asList("architect"))); } return INVALID_RESULT; } } ================================================ FILE: test/embedded/soteria-basic/src/test/resources/META-INF/beans.xml ================================================ ================================================ FILE: test/embedded/soteria-form/pom.xml ================================================ 4.0.0 cloud.piranha.test.embedded project 25.4.0-SNAPSHOT piranha-test-embedded-soteria-form war Piranha - Test - Embedded - Soteria FORM jakarta.platform jakarta.jakartaee-web-api provided cloud.piranha piranha-embedded ${project.version} test cloud.piranha.extension piranha-extension-exousia ${project.version} test cloud.piranha.extension piranha-extension-herring ${project.version} test cloud.piranha.extension piranha-extension-security-jakarta ${project.version} test cloud.piranha.extension piranha-extension-webxml ${project.version} test cloud.piranha.extension piranha-extension-weld ${project.version} test org.junit.jupiter junit-jupiter-api test ================================================ FILE: test/embedded/soteria-form/src/test/java/form/DynamicInitialContextFactory.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package form; import java.lang.reflect.Field; import java.util.Hashtable; import javax.naming.Context; import javax.naming.NamingException; import javax.naming.spi.InitialContextFactory; import com.manorrock.herring.DefaultInitialContext; /** * The default InitialContextFactory. * * @author Manfred Riem (mriem@manorrock.com) * @author Arjan Tijms */ public class DynamicInitialContextFactory implements InitialContextFactory { /** * Stores the initial context. */ private static final DefaultInitialContext INITIAL_CONTEXT = new DefaultInitialContext(); /** * {@return the initial context} * @param environment the environment. * @throws NamingException when a naming error occurs. */ @Override public Context getInitialContext(Hashtable environment) throws NamingException { try { Field closedField = DefaultInitialContext.class.getDeclaredField("closed"); closedField.setAccessible(true); closedField.setBoolean(INITIAL_CONTEXT, false); } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); } return INITIAL_CONTEXT; } } ================================================ FILE: test/embedded/soteria-form/src/test/java/form/ErrorPageServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package form; import java.io.IOException; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * * @author Arjan Tijms */ public class ErrorPageServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write( "" + "" + "

" + "Authentication did not succeed!" + "

" + "" + ""); } } ================================================ FILE: test/embedded/soteria-form/src/test/java/form/FormTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package form; import static cloud.piranha.extension.exousia.AuthorizationPreInitializer.CONSTRAINTS; import static java.util.Arrays.asList; import static javax.naming.Context.INITIAL_CONTEXT_FACTORY; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.URL; import org.glassfish.exousia.constraints.SecurityConstraint; import org.junit.jupiter.api.Test; import cloud.piranha.core.impl.DefaultServlet; import cloud.piranha.embedded.EmbeddedPiranha; import cloud.piranha.embedded.EmbeddedPiranhaBuilder; import cloud.piranha.embedded.EmbeddedRequest; import cloud.piranha.embedded.EmbeddedRequestBuilder; import cloud.piranha.embedded.EmbeddedResponse; import cloud.piranha.extension.exousia.AuthorizationPostInitializer; import cloud.piranha.extension.herring.HerringExtension; import cloud.piranha.extension.security.jakarta.JakartaSecurityAllInitializer; import cloud.piranha.extension.security.servlet.ServletSecurityManagerExtension; import cloud.piranha.extension.webxml.WebXmlInitializer; import jakarta.security.enterprise.authentication.mechanism.http.FormAuthenticationMechanismDefinition; import jakarta.security.enterprise.authentication.mechanism.http.LoginToContinue; import jakarta.servlet.http.Cookie; import org.junit.jupiter.api.Disabled; /** * * @author Arjan Tijms */ @FormAuthenticationMechanismDefinition( loginToContinue = @LoginToContinue( loginPage = "/login-page", errorPage = "/error-page")) @Disabled class FormTest { @Test void testAuthenticated() throws Exception { System.getProperties().put(INITIAL_CONTEXT_FACTORY, DynamicInitialContextFactory.class.getName()); EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .attribute(CONSTRAINTS, asList( new SecurityConstraint("/protected/servlet", "architect"))) .extensions( ServletSecurityManagerExtension.class, HerringExtension.class) .initializers( WebXmlInitializer.class, JakartaSecurityAllInitializer.class, AuthorizationPostInitializer.class) .servletsMapped( ProtectedServlet.class, "/protected/servlet", ErrorPageServlet.class, "/error-page", LoginPageServlet.class, "/login-page", DefaultServlet.class, "/*") .buildAndStart(); EmbeddedResponse response = piranha.service(new EmbeddedRequest("/protected/servlet")); assertTrue( response.getResponseAsString().contains( "Enter name and password to authenticate"), "Should have received login page, but did not" ); Cookie sessionCookie = response.getCookies().iterator().next(); EmbeddedRequest request = new EmbeddedRequestBuilder() .method("POST") .servletPath("/j_security_check") .parameter("j_username", "test") .parameter("j_password", "test") .requestedSessionId(sessionCookie.getValue()) .requestedSessionIdFromCookie(true) .cookie(sessionCookie) .build(); response = piranha.service(request); assertEquals(302, response.getStatus(), "Should redirect"); @SuppressWarnings("deprecation") URL redirectUrl = new URL(response.getHeader("Location")); request = new EmbeddedRequestBuilder() .servletPath(redirectUrl.getPath()) .requestedSessionId(sessionCookie.getValue()) .requestedSessionIdFromCookie(true) .cookie(sessionCookie) .build(); response = piranha.service(request); // Not only does the page needs to be accessible, the caller should have // the correct // name and roles as well // Being able to access a page protected by a role but then seeing the un-authenticated // (anonymous) user would normally be impossible, but could happen if the authorization // system checks roles on the authenticated subject, but does not correctly expose // or propagate these to the HttpServletRequest assertFalse( response.getResponseAsString().contains("web username: null"), "Protected resource could be accessed, but the user appears to be the unauthenticated user. " + "This should not be possible" ); // An authenticated user should have the exact name "test" and nothing else. assertTrue( response.getResponseAsString().contains("web username: test"), "Protected resource could be accessed, but the username is not correct." ); // Being able to access a page protected by role "architect" but failing // the test for this role would normally be impossible, but could happen if the // authorization system checks roles on the authenticated subject, but does not // correctly expose or propagate these to the HttpServletRequest assertTrue( response.getResponseAsString().contains("web user has role \"architect\": true"), "Resource protected by role \"architect\" could be accessed, but user fails test for this role." + "This should not be possible" ); request = new EmbeddedRequestBuilder() .servletPath("/protected/servlet") .requestedSessionId(sessionCookie.getValue()) .requestedSessionIdFromCookie(true) .cookie(sessionCookie) .build(); response = piranha.service(request); // Being able to access a page protected by a role but then seeing the un-authenticated // (anonymous) user would normally be impossible, but could happen if the authorization // system checks roles on the authenticated subject, but does not correctly expose // or propagate these to the HttpServletRequest assertFalse( response.getResponseAsString().contains("web username: null"), "Protected resource could be accessed, but the user appears to be the unauthenticated user. " + "This should not be possible" ); // An authenticated user should have the exact name "test" and nothing else. assertTrue( response.getResponseAsString().contains("web username: test"), "Protected resource could be accessed, but the username is not correct." ); // Being able to access a page protected by role "architect" but failing // the test for this role would normally be impossible, but could happen if the // authorization system checks roles on the authenticated subject, but does not // correctly expose or propagate these to the HttpServletRequest assertTrue( response.getResponseAsString().contains("web user has role \"architect\": true"), "Resource protected by role \"architect\" could be accessed, but user fails test for this role." + "This should not be possible" ); } } ================================================ FILE: test/embedded/soteria-form/src/test/java/form/LoginPageServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package form; import java.io.IOException; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * * @author Arjan Tijms */ public class LoginPageServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write( "" + "" + "

" + "Enter name and password to authenticate" + "

" + "
" + "" + "" + "" + "
" + "" + ""); } } ================================================ FILE: test/embedded/soteria-form/src/test/java/form/ProtectedServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package form; import java.io.IOException; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * * @author Arjan Tijms * */ @WebServlet(urlPatterns = "/protected/servlet") public class ProtectedServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("This is a protected servlet \n"); String webName = null; if (request.getUserPrincipal() != null) { webName = request.getUserPrincipal().getName(); } response.getWriter().write("web username: " + webName + "\n"); boolean webHasRole = request.isUserInRole("architect"); response.getWriter().write("web user has role \"architect\": " + webHasRole + "\n"); } } ================================================ FILE: test/embedded/soteria-form/src/test/java/form/TestIdentityStore.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package form; import static java.util.Arrays.asList; import static jakarta.security.enterprise.identitystore.CredentialValidationResult.INVALID_RESULT; import java.util.HashSet; import jakarta.enterprise.context.ApplicationScoped; import jakarta.security.enterprise.credential.UsernamePasswordCredential; import jakarta.security.enterprise.identitystore.CredentialValidationResult; import jakarta.security.enterprise.identitystore.IdentityStore; /** * * @author Arjan Tijms * */ @ApplicationScoped public class TestIdentityStore implements IdentityStore { public CredentialValidationResult validate(UsernamePasswordCredential usernamePasswordCredential) { if (usernamePasswordCredential.compareTo("test", "test")) { return new CredentialValidationResult("test", new HashSet<>(asList("architect"))); } return INVALID_RESULT; } } ================================================ FILE: test/embedded/soteria-form/src/test/resources/META-INF/beans.xml ================================================ ================================================ FILE: test/embedded/springboot/pom.xml ================================================ 4.0.0 cloud.piranha.test.embedded project 25.4.0-SNAPSHOT cloud.piranha.test.embedded piranha-test-embedded-springboot Piranha - Test - Embedded - Spring Boot org.springframework.boot spring-boot-dependencies ${spring-boot.version} pom import org.springframework.boot spring-boot-starter-web compile org.springframework.boot spring-boot-starter-tomcat cloud.piranha.spring piranha-embedded-spring-boot-starter ${project.version} compile org.springframework.boot spring-boot-maven-plugin ================================================ FILE: test/embedded/springboot/src/main/java/test1/Test1Application.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package test1; import cloud.piranha.spring.starter.embedded.EmbeddedPiranhaServletWebServerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.context.annotation.Bean; /** * The Spring boot test application. * * @author Manfred Riem (mriem@manorrock.com) */ @SpringBootApplication public class Test1Application { /** * Create the Piranha Embedded ServletWebServerFactory. * * @return the factory. */ @Bean public ServletWebServerFactory factory() { return new EmbeddedPiranhaServletWebServerFactory(); } /** * Customize the Piranha Embedded integration. * * @return the customizer. */ @Bean public WebServerFactoryCustomizer customizer() { return new WebServerFactoryCustomizer() { /** * Customize the factory. * * @param factory the factory to customize. */ @Override public void customize(EmbeddedPiranhaServletWebServerFactory factory) { factory.setPort(8080); } }; } /** * Main method. * * @param arguments the command-line arguments. */ public static void main(String[] arguments) { SpringApplication.run(Test1Application.class, arguments); } } ================================================ FILE: test/embedded/springboot/src/main/java/test1/Test1Controller.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package test1; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class Test1Controller { /** * Get 'Hello World!'. * * @return 'Hello World!'. */ @GetMapping("/hello") public String hello() { return "Hello World!"; } } ================================================ FILE: test/embedded/springboot/src/main/resources/application.properties ================================================ ================================================ FILE: test/embedded/springboot-virtualthreads/pom.xml ================================================ 4.0.0 cloud.piranha.test.embedded project 25.4.0-SNAPSHOT cloud.piranha.test.embedded piranha-test-embedded-springboot-virtualthreads 25.4.0-SNAPSHOT Piranha - Test - Embedded - Spring Boot Virtual Threads org.springframework.boot spring-boot-dependencies ${spring-boot.version} pom import org.springframework.boot spring-boot-starter-web compile org.springframework.boot spring-boot-starter-tomcat cloud.piranha.spring piranha-embedded-spring-boot-starter ${project.version} compile cloud.piranha.http piranha-http-virtual ${project.version} compile org.springframework.boot spring-boot-maven-plugin --enable-preview jdk21 21 org.apache.maven.plugins maven-compiler-plugin ${java.version} true jdk23 23 org.apache.maven.plugins maven-compiler-plugin 23 true ================================================ FILE: test/embedded/springboot-virtualthreads/src/main/java/test/custom/http/Main.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package test.custom.http; import cloud.piranha.http.virtual.VirtualHttpServer; import cloud.piranha.spring.starter.embedded.EmbeddedPiranhaServletWebServerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.context.annotation.Bean; @SpringBootApplication public class Main { /** * Create the Piranha Embedded ServletWebServerFactory. * * @return the factory. */ @Bean public ServletWebServerFactory factory() { return new EmbeddedPiranhaServletWebServerFactory(); } /** * Customize the Piranha Embedded integration. * * @return the customizer. */ @Bean public WebServerFactoryCustomizer customizer() { return factory -> factory.setHttpServer(new VirtualHttpServer()); } /** * Main method. * * @param arguments the command-line arguments. */ public static void main(String[] arguments) { SpringApplication.run(Main.class, arguments); } } ================================================ FILE: test/embedded/springboot-virtualthreads/src/main/java/test/custom/http/MyController.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package test.custom.http; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Map; @RestController @RequestMapping("/endpoint") public class MyController { /** * Hello world. * * @return the hello world map. */ @GetMapping public Map helloWorld() { return Map.of("hello", "world", "isVirtual", Boolean.toString(Thread.currentThread().isVirtual())); } } ================================================ FILE: test/embedded/web-fragment-in-jar/pom.xml ================================================ 4.0.0 cloud.piranha.test.embedded project 25.4.0-SNAPSHOT piranha-test-embedded-web-fragment-in-jar jar Piranha - Test - Embedded - web-fragment in JAR ================================================ FILE: test/embedded/web-fragment-in-jar/src/main/resources/META-INF/web-fragment.xml ================================================ /webfragmentInJar ================================================ FILE: test/embedded/web-fragment-in-jar-test/pom.xml ================================================ 4.0.0 cloud.piranha.test.embedded project 25.4.0-SNAPSHOT pirnaha-test-embedded-web-fragment-in-jar-test jar Piranha - Test - Embedded - web-fragment in JAR test cloud.piranha.core piranha-core-impl ${project.version} test cloud.piranha.extension piranha-extension-webxml ${project.version} test cloud.piranha.test.embedded piranha-test-embedded-web-fragment-in-jar-webapp ${project.version} war test org.junit.jupiter junit-jupiter-api test org.apache.maven.plugins maven-install-plugin default-install none org.apache.maven.plugins maven-jar-plugin default-jar none org.apache.maven.plugins maven-dependency-plugin unpack pre-integration-test unpack cloud.piranha.test.embedded piranha-test-embedded-web-fragment-in-jar-webapp ${project.version} war true ${project.build.directory}/webapp ================================================ FILE: test/embedded/web-fragment-in-jar-test/src/test/java/webfragmentinjar/WebFragmentInJar1IT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package webfragmentinjar; import cloud.piranha.core.impl.DefaultWebApplication; import cloud.piranha.core.impl.DefaultWebApplicationClassLoader; import cloud.piranha.extension.webxml.WebXmlInitializer; import java.io.File; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; /** * The integration tests for the web-fragment.xml support. * * @author Manfred Riem (mriem@manorrock.com) */ class WebFragmentInJar1IT { /** * Test web-fragment.xml support. * * @throws Exception when a serious error occurs. */ @Test void testLoadClass() throws Exception { DefaultWebApplicationClassLoader classLoader = new DefaultWebApplicationClassLoader(new File("target/webapp")); DefaultWebApplication webApplication = new DefaultWebApplication(); webApplication.setClassLoader(classLoader); webApplication.addInitializer(new WebXmlInitializer()); webApplication.initialize(); assertEquals("/webfragmentInJar", webApplication.getContextPath()); } } ================================================ FILE: test/embedded/web-fragment-in-jar-webapp/pom.xml ================================================ 4.0.0 cloud.piranha.test.embedded project 25.4.0-SNAPSHOT piranha-test-embedded-web-fragment-in-jar-webapp war Piranha - Test - Embedded - web-fragment in JAR webapp UTF-8 cloud.piranha.test.embedded piranha-test-embedded-web-fragment-in-jar ${project.version} ================================================ FILE: test/embedded/web-fragment-in-jar-webapp/src/main/webapp/META-INF/context.xml ================================================ ================================================ FILE: test/embedded/web-fragment-in-jar-webapp/src/main/webapp/index.html ================================================ Start Page

Hello World!

================================================ FILE: test/embedded/weld/pom.xml ================================================ 4.0.0 cloud.piranha.test.embedded project 25.4.0-SNAPSHOT piranha-test-embedded-weld war Piranha - Test - Embedded - Weld UTF-8 jakarta.enterprise jakarta.enterprise.cdi-api provided cloud.piranha.extension piranha-extension-mojarra ${project.version} runtime cloud.piranha.extension piranha-extension-scinitializer ${project.version} runtime cloud.piranha.extension piranha-extension-weld ${project.version} runtime cloud.piranha.extension piranha-extension-webxml ${project.version} runtime cloud.piranha piranha-embedded ${project.version} test cloud.piranha.extension piranha-extension-expressly ${project.version} test cloud.piranha.extension piranha-extension-jstl ${project.version} test cloud.piranha.extension piranha-extension-wasp ${project.version} test cloud.piranha.extension piranha-extension-herring ${project.version} test jakarta.websocket jakarta.websocket-api test jakarta.websocket jakarta.websocket-client-api test org.junit.jupiter junit-jupiter-api test org.apache.maven.plugins maven-war-plugin false ================================================ FILE: test/embedded/weld/src/main/java/weld/WeldRequestBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package weld; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Named; /** * A simple request bean. * * @author Manfred Riem (mriem@manorrock.com) */ @Named(value = "requestBean") @RequestScoped public class WeldRequestBean { /** * Stores the HELLO constant. */ private static final String HELLO = "Hello Weld!"; /** * Get the "Hello Weld". * * @return "Hello Weld" */ public String getHello() { return HELLO; } } ================================================ FILE: test/embedded/weld/src/main/webapp/WEB-INF/beans.xml ================================================ ================================================ FILE: test/embedded/weld/src/main/webapp/WEB-INF/faces-config.xml ================================================ org.jboss.weld.module.jsf.ConversationAwareViewHandler ================================================ FILE: test/embedded/weld/src/main/webapp/WEB-INF/web.xml ================================================ Faces Servlet jakarta.faces.webapp.FacesServlet 1 Faces Servlet *.html ================================================ FILE: test/embedded/weld/src/main/webapp/index.xhtml ================================================ ================================================ FILE: test/embedded/weld/src/test/java/weld/WeldTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package weld; import cloud.piranha.embedded.EmbeddedPiranha; import cloud.piranha.embedded.EmbeddedPiranhaBuilder; import cloud.piranha.embedded.EmbeddedRequest; import cloud.piranha.embedded.EmbeddedRequestBuilder; import cloud.piranha.embedded.EmbeddedResponse; import cloud.piranha.extension.herring.HerringExtension; import cloud.piranha.extension.herring.HerringInitialContextFactory; import cloud.piranha.extension.scinitializer.ServletContainerInitializerExtension; import cloud.piranha.extension.webxml.WebXmlExtension; import static javax.naming.Context.INITIAL_CONTEXT_FACTORY; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * The JUnit tests for the Hello Weld web application. * * @author Manfred Riem (mriem@manorrock.com) */ class WeldTest { /** * Test /index.html. * * @throws Exception */ @Test void testIndexHtml() throws Exception { System.getProperties().put(INITIAL_CONTEXT_FACTORY, HerringInitialContextFactory.class.getName()); EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder() .directoryResource("src/main/webapp") .aliasedDirectoryResource("target/classes", "/WEB-INF/classes") .extension(HerringExtension.class) .extension(WebXmlExtension.class) .extension(ServletContainerInitializerExtension.class) .build() .start(); EmbeddedRequest request = new EmbeddedRequestBuilder() .servletPath("/index.html") .build(); EmbeddedResponse response = new EmbeddedResponse(); piranha.service(request, response); assertEquals(200, response.getStatus()); assertTrue(response.getResponseAsString().contains("Hello Weld")); piranha.stop() .destroy(); } } ================================================ FILE: test/jpms/pom.xml ================================================ 4.0.0 cloud.piranha.test project 25.4.0-SNAPSHOT piranha-test-jpms war Piranha - Test - JPMS cloud.piranha.resource piranha-resource-api ${project.version} compile cloud.piranha.resource piranha-resource-shrinkwrap ${project.version} test org.jboss.shrinkwrap shrinkwrap-impl-base test org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test cloud.piranha.core piranha-core-impl ${project.version} test ================================================ FILE: test/jpms/src/test/java/jpms/ModuleFinderTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package jpms; import cloud.piranha.core.impl.DefaultModuleFinder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import cloud.piranha.resource.api.Resource; import cloud.piranha.resource.shrinkwrap.ShrinkWrapResource; import org.jboss.shrinkwrap.api.asset.ByteArrayAsset; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleReference; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Set; import static org.jboss.shrinkwrap.api.ShrinkWrap.create; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; class ModuleFinderTest { private Resource createResource(JavaArchive jar, String name) { return new ShrinkWrapResource("", jar, name); } @Test void testFindAutomaticModuleWithoutClasses() { Resource module1 = createResource(create(JavaArchive.class), "module1"); Set moduleReferences = new DefaultModuleFinder(List.of(module1)).findAll(); assertNotNull(moduleReferences); assertEquals(1, moduleReferences.size()); String moduleName = moduleReferences.stream() .findFirst() .map(ModuleReference::descriptor) .map(ModuleDescriptor::name) .orElseThrow(); assertEquals("module1", moduleName); } @Test void testFindAutomaticModuleWithClass() { Resource module1 = createResource(create(JavaArchive.class).addClass(package1.Library.class), "module1"); Set moduleReferences = new DefaultModuleFinder(List.of(module1)).findAll(); assertNotNull(moduleReferences); assertEquals(1, moduleReferences.size()); ModuleReference moduleReference = moduleReferences.stream().findFirst().orElseThrow(); ModuleDescriptor descriptor = moduleReference.descriptor(); assertEquals("module1", descriptor.name()); Set packages = descriptor.packages(); assertEquals(1, packages.size()); assertTrue(packages.contains("package1")); } @Test void testFindMultiPackagesSingleModule() { JavaArchive jar = create(JavaArchive.class) .addClass(package1.Library.class) .addClass(package2.Listener.class) .addClass(package3.Filter.class); Resource module1 = createResource(jar, "module1"); Set moduleReferences = new DefaultModuleFinder(List.of(module1)).findAll(); assertNotNull(moduleReferences); assertEquals(1, moduleReferences.size()); ModuleReference moduleReference = moduleReferences.stream().findFirst().orElseThrow(); ModuleDescriptor descriptor = moduleReference.descriptor(); assertEquals("module1", descriptor.name()); Set packages = descriptor.packages(); assertEquals(3, packages.size()); assertAll(() -> assertTrue(packages.contains("package1")), () -> assertTrue(packages.contains("package2")), () -> assertTrue(packages.contains("package3"))); } @Test void testSplitPackagesTwoJars() { JavaArchive jar1 = create(JavaArchive.class).addClass(package1.Library.class); JavaArchive jar2 = create(JavaArchive.class).addClass(package1.Servlet.class); Resource module1 = createResource(jar1, "module1"); Resource module2 = createResource(jar2, "module2"); Set moduleReferences = new DefaultModuleFinder(List.of(module1, module2)).findAll(); assertNotNull(moduleReferences); assertEquals(0, moduleReferences.size()); } @Test void testSplitPackagesThreeJars() { JavaArchive jar1 = create(JavaArchive.class).addClass(package1.Library.class).addClass(package3.Library.class); JavaArchive jar2 = create(JavaArchive.class).addClass(package1.Utils.class).addClass(package2.Listener.class); JavaArchive jar3 = create(JavaArchive.class).addClass(package2.Library.class).addClass(package3.Filter.class); Resource module1 = createResource(jar1, "module1"); Resource module2 = createResource(jar2, "module2"); Resource module3 = createResource(jar3, "module3"); Set moduleReferences = new DefaultModuleFinder(List.of(module1, module2, module3)).findAll(); assertNotNull(moduleReferences); assertEquals(0, moduleReferences.size()); } @Test void testSplitPackagesAndRegular() { JavaArchive jar1 = create(JavaArchive.class).addClass(package1.Library.class).addClass(package1.Servlet.class); JavaArchive jar2 = create(JavaArchive.class).addClass(package2.Listener.class).addClass(package2.Library.class); JavaArchive jar3 = create(JavaArchive.class).addClass(package1.Utils.class).addClass(package3.Filter.class); Resource module1 = createResource(jar1, "module1"); Resource module2 = createResource(jar2, "module2"); Resource module3 = createResource(jar3, "module3"); Set moduleReferences = new DefaultModuleFinder(List.of(module1, module2, module3)).findAll(); assertNotNull(moduleReferences); assertEquals(1, moduleReferences.size()); assertEquals(1, moduleReferences.size()); moduleReferences.stream().findFirst().ifPresentOrElse(x -> assertEquals("module2", x.descriptor().name()), Assertions::fail); } @ParameterizedTest @CsvSource({ "module.jar,module", "module-test-foo.jar,module.test.foo", "module-version4.jar,module.version4", "module-foo2-bar.jar,module.foo2.bar", "module1-test.jar,module1.test" }) void testAutomaticModuleNamesWithoutVersion(String jarName, String moduleName) { Resource module = createResource(create(JavaArchive.class), jarName); Set moduleReferences = new DefaultModuleFinder(List.of(module)).findAll(); ModuleDescriptor moduleDescriptor = moduleReferences.stream().findFirst().map(ModuleReference::descriptor).orElseThrow(); assertEquals(moduleName, moduleDescriptor.name()); assertTrue(moduleDescriptor.version().isEmpty()); } @ParameterizedTest @CsvSource({ "module-4.0.0.jar,module,4.0.0", "module-test-foo-2.jar,module.test.foo,2", "module-test-bar-1.0-SNAPSHOT.jar,module.test.bar,1.0-SNAPSHOT", "module-test-baz-7.3.Final.jar,module.test.baz,7.3.Final" }) void testAutomaticModuleNamesWithVersion(String jarName, String moduleName, String version) { Resource module = createResource(create(JavaArchive.class), jarName); Set moduleReferences = new DefaultModuleFinder(List.of(module)).findAll(); ModuleDescriptor moduleDescriptor = moduleReferences.stream().findFirst().map(ModuleReference::descriptor).orElseThrow(); assertEquals(moduleName, moduleDescriptor.name()); assertEquals(version, moduleDescriptor.version().map(ModuleDescriptor.Version::toString).orElseThrow()); } @Test void testServices() { JavaArchive archive = create(JavaArchive.class).addAsServiceProvider(package1.Service.class, package3.DefaultService.class); Resource module = createResource(archive, "module"); Set moduleReferences = new DefaultModuleFinder(List.of(module)).findAll(); ModuleDescriptor moduleDescriptor = moduleReferences.stream().findFirst().map(ModuleReference::descriptor).orElseThrow(); assertEquals("module", moduleDescriptor.name()); Set provides = moduleDescriptor.provides(); assertEquals(1, provides.size()); ModuleDescriptor.Provides provide = provides.stream().findFirst().orElseThrow(); assertEquals(package1.Service.class.getName(), provide.service()); assertEquals(1, provide.providers().size()); assertTrue(provide.providers().contains(package3.DefaultService.class.getName())); } @Test void testInvalidServices() { JavaArchive archive = create(JavaArchive.class).addAsServiceProvider("package1.Service", "invalid-name"); Resource module = createResource(archive, "module"); Set moduleReferences = new DefaultModuleFinder(List.of(module)).findAll(); assertEquals(0, moduleReferences.size()); } @ParameterizedTest @CsvSource({ "jar-old-name,module.foo.bar", "enum-new-package,valid.module" }) void testModuleNameFromManifest(String jarName, String automaticModuleName) { String manifestContent = """ Manifest-Version: 1.0 Automatic-Module-Name: %s """.formatted(automaticModuleName); ByteArrayAsset manifest = new ByteArrayAsset(manifestContent.getBytes(StandardCharsets.UTF_8)); JavaArchive archive = create(JavaArchive.class).addAsManifestResource(manifest, "MANIFEST.MF"); Resource module = createResource(archive, jarName); Set moduleReferences = new DefaultModuleFinder(List.of(module)).findAll(); assertEquals(1, moduleReferences.size()); String moduleName = moduleReferences.stream() .findFirst() .map(ModuleReference::descriptor) .map(ModuleDescriptor::name) .orElseThrow(); assertEquals(automaticModuleName, moduleName); } @ParameterizedTest @CsvSource({ "jar-old-name,jar.new.name", "enum-new-package,enum.new.package" }) void testInvalidModuleNameFromManifest(String jarName, String automaticModuleName) { String manifestContent = """ Manifest-Version: 1.0 Automatic-Module-Name: %s """.formatted(automaticModuleName); ByteArrayAsset manifest = new ByteArrayAsset(manifestContent.getBytes(StandardCharsets.UTF_8)); JavaArchive archive = create(JavaArchive.class).addAsManifestResource(manifest, "MANIFEST.MF"); Resource module = createResource(archive, jarName); Set moduleReferences = new DefaultModuleFinder(List.of(module)).findAll(); assertEquals(0, moduleReferences.size()); } } ================================================ FILE: test/jpms/src/test/java/package1/Library.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package package1; public class Library { } ================================================ FILE: test/jpms/src/test/java/package1/Service.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package package1; public interface Service { } ================================================ FILE: test/jpms/src/test/java/package1/Servlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package package1; public class Servlet { } ================================================ FILE: test/jpms/src/test/java/package1/Utils.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package package1; public class Utils { } ================================================ FILE: test/jpms/src/test/java/package2/Library.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package package2; public class Library { } ================================================ FILE: test/jpms/src/test/java/package2/Listener.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package package2; public class Listener { } ================================================ FILE: test/jpms/src/test/java/package3/DefaultService.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package package3; import package1.Service; public class DefaultService implements Service { } ================================================ FILE: test/jpms/src/test/java/package3/Filter.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package package3; public class Filter { } ================================================ FILE: test/jpms/src/test/java/package3/Library.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package package3; public class Library { } ================================================ FILE: test/micro/helloworld/pom.xml ================================================ 4.0.0 cloud.piranha.test.micro project 25.4.0-SNAPSHOT piranha-test-micro-helloworld war Piranha - Test - Micro - Hello World application 21 micro UTF-8 piranha-test-micro-helloworld org.apache.maven.plugins maven-compiler-plugin ${java.version} org.apache.maven.plugins maven-failsafe-plugin integration-test verify ${httpPort} org.apache.maven.plugins maven-war-plugin false org.codehaus.mojo build-helper-maven-plugin reserve-network-port reserve-network-port validate httpPort jakarta.platform jakarta.jakartaee-web-api provided org.junit.jupiter junit-jupiter test ================================================ FILE: test/micro/helloworld/src/main/java/helloworld/HelloWorldServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package helloworld; import java.io.IOException; import java.io.PrintWriter; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; public class HelloWorldServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try (PrintWriter writer = response.getWriter()) { response.setContentType("text/plain"); writer.println("Hello World!"); writer.flush(); } } } ================================================ FILE: test/micro/helloworld/src/main/webapp/WEB-INF/web.xml ================================================ HelloWorldServlet helloworld.HelloWorldServlet HelloWorldServlet /index.html 30 ================================================ FILE: test/micro/helloworld/src/test/java/helloworld/HelloWorldServletIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package helloworld; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; class HelloWorldServletIT { @Disabled @Test void testHelloWorld() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + Integer.valueOf(System.getProperty("httpPort")) + "/piranha-test-micro-helloworld/index.html")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello World!")); } } ================================================ FILE: test/micro/microprofile-config/pom.xml ================================================ 4.0.0 cloud.piranha.test.micro project 25.4.0-SNAPSHOT piranha-test-micro-microprofile-config war Piranha - Test - Micro - MicroProfile Config org.jboss.arquillian arquillian-bom ${arquillian.version} pom import org.eclipse.microprofile.config microprofile-config-api compile jakarta.platform jakarta.jakartaee-web-api provided cloud.piranha.arquillian piranha-arquillian-server ${project.version} test org.htmlunit htmlunit test org.jboss.arquillian.junit arquillian-junit-container test org.junit.jupiter junit-jupiter-api test org.junit.vintage junit-vintage-engine test ================================================ FILE: test/micro/microprofile-config/src/main/java/config/ConfigApplicationBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package config; import java.util.Optional; import org.eclipse.microprofile.config.inject.ConfigProperty; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; @ApplicationScoped public class ConfigApplicationBean { /** * Example showing injection of a default property */ @Inject @ConfigProperty(name = "default.property", defaultValue = "Default Value") private String defaultProperty; /** * Example showing reading a config property from the META-INF/microprofile-config.properties file */ @Inject @ConfigProperty(name = "file.property") private String fileProperty; /** * Example showing reading a config property from the META-INF/microprofile-config.properties file */ @Inject @ConfigProperty(name = "application.property") private String applicationProperty; /** * Example injection of an optional value that's not required to be present */ @Inject @ConfigProperty(name = "application.optionalProperty") private Optional optionalApplicationProperty; /** * * @return defaultProperty */ public String getDefaultProperty() { return defaultProperty; } /** * * @return fileProperty */ public String getFileProperty() { return fileProperty; } /** * * @return applicationProperty */ public String getApplicationProperty() { return applicationProperty; } /** * * @return optionalApplicationProperty */ public Optional getOptionalApplicationProperty() { return optionalApplicationProperty; } } ================================================ FILE: test/micro/microprofile-config/src/main/java/config/ConfigServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package config; import java.io.IOException; import jakarta.inject.Inject; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @WebServlet("/servlet") public class ConfigServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * ConfigApplicationBean */ @Inject private ConfigApplicationBean applicationBean; /** * */ @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/plain"); response.getWriter().write( "default.property : " + applicationBean.getDefaultProperty() + "\n" + "file.property : " + applicationBean.getFileProperty() + "\n" + "application.property : " + applicationBean.getApplicationProperty() + "\n" + "application.optionalProperty : " + applicationBean.getOptionalApplicationProperty().orElse("Not defined") + "\n" ); } } ================================================ FILE: test/micro/microprofile-config/src/main/resources/META-INF/microprofile-config.properties ================================================ # Example application properties file file.property=File Property date.property=2000-01-01 application.property=Some value ================================================ FILE: test/micro/microprofile-config/src/test/java/config/ConfigTest.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package config; import static org.jboss.shrinkwrap.api.ShrinkWrap.create; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.net.URL; import org.htmlunit.TextPage; import org.htmlunit.WebClient; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.RunAsClient; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.jboss.arquillian.junit.Arquillian; import org.jboss.arquillian.test.api.ArquillianResource; import org.junit.runner.RunWith; /** * @author Arjan Tijms */ @RunWith(Arquillian.class) public class ConfigTest { @ArquillianResource private URL base; WebClient webClient; @Deployment(testable = false) public static WebArchive createDeployment() { WebArchive archive = create(WebArchive.class) .addClasses(ConfigApplicationBean.class, ConfigServlet.class ).addAsResource( "META-INF/microprofile-config.properties" ) ; System.out.println("************************************************************"); System.out.println(archive.toString(true)); System.out.println("************************************************************"); return archive; } @Before public void setup() { webClient = new WebClient(); } @After public void tearDown() { webClient.getCookieManager().clearCookies(); webClient.close(); } @Test @RunAsClient public void testBasicInjection() throws IOException { TextPage page = webClient.getPage(base + "servlet"); String response = page.getContent(); System.out.println("-------------------------------------------------------------------------"); System.out.println("Response: " + response); System.out.println("-------------------------------------------------------------------------"); assertTrue( response.contains("default.property : Default Value") ); assertTrue( response.contains("file.property : File Property") ); assertTrue( response.contains("application.property : Some value") ); } @Test @RunAsClient public void testOptional() throws IOException { TextPage page = webClient.getPage(base + "servlet"); String response = page.getContent(); System.out.println("-------------------------------------------------------------------------"); System.out.println("Response: " + response); System.out.println("-------------------------------------------------------------------------"); assertTrue( response.contains("application.optionalProperty : Not defined") ); } } ================================================ FILE: test/micro/pom.xml ================================================ 4.0.0 cloud.piranha.test project 25.4.0-SNAPSHOT cloud.piranha.test.micro project pom Piranha - Test - Micro - Project helloworld microprofile-config protected-servlet snoop cloud.piranha.dist piranha-dist-micro ${project.version} test me.alexpanov free-port-finder test ================================================ FILE: test/micro/protected-servlet/pom.xml ================================================ 4.0.0 cloud.piranha.test.micro project 25.4.0-SNAPSHOT piranha-test-micro-protected-servlet war Piranha - Test - Micro - Protected Servlet org.jboss.arquillian arquillian-bom ${arquillian.version} pom import jakarta.platform jakarta.jakartaee-web-api provided cloud.piranha.arquillian piranha-arquillian-server ${project.version} test org.htmlunit htmlunit test org.jboss.arquillian.junit arquillian-junit-container test org.junit.jupiter junit-jupiter-api test org.junit.vintage junit-vintage-engine test ================================================ FILE: test/micro/protected-servlet/src/main/java/protectedservlet/ProtectedBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package protectedservlet; import jakarta.enterprise.context.RequestScoped; /** * A CDI bean. * * @author Manfred Riem (mriem@manorrock.com) */ @RequestScoped public class ProtectedBean { } ================================================ FILE: test/micro/protected-servlet/src/main/java/protectedservlet/ProtectedServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package protectedservlet; import java.io.IOException; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * * @author Arjan Tijms * */ @WebServlet(urlPatterns = "/protected/servlet") public class ProtectedServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("This is a protected servlet \n"); String webName = null; if (request.getUserPrincipal() != null) { webName = request.getUserPrincipal().getName(); } response.getWriter().write("web username: " + webName + "\n"); boolean webHasRole = request.isUserInRole("architect"); response.getWriter().write("web user has role \"architect\": " + webHasRole + "\n"); } } ================================================ FILE: test/micro/protected-servlet/src/main/webapp/WEB-INF/beans.xml ================================================ ================================================ FILE: test/micro/protected-servlet/src/main/webapp/WEB-INF/web.xml ================================================ ProtectedServlet /protected/servlet GET architect BASIC some ================================================ FILE: test/micro/protected-servlet/src/test/java/protectedservlet/ProtectedServletIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package protectedservlet; import static org.jboss.shrinkwrap.api.ShrinkWrap.create; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.net.URL; import org.htmlunit.DefaultCredentialsProvider; import org.htmlunit.FailingHttpStatusCodeException; import org.htmlunit.TextPage; import org.htmlunit.WebClient; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; import org.jboss.arquillian.test.api.ArquillianResource; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(Arquillian.class) public class ProtectedServletIT { @ArquillianResource private URL base; WebClient webClient; @Deployment(testable = false) public static WebArchive createDeployment() { System.setProperty( "io.piranha.identitystore.callers", ""); return create(WebArchive.class). addClass(ProtectedServlet.class). addClass(ProtectedBean.class). addAsWebInfResource(new File("src/main/webapp/WEB-INF", "beans.xml")). addAsWebInfResource((new File("src/main/webapp/WEB-INF", "web.xml"))); } @Before public void setup() { webClient = new WebClient(); } @After public void tearDown() { webClient.getCookieManager().clearCookies(); webClient.close(); } @Test public void testGetWithCorrectCredentials() throws Exception { DefaultCredentialsProvider credentialsProvider = new DefaultCredentialsProvider(); credentialsProvider.addCredentials("test", "pass".toCharArray()); webClient.setCredentialsProvider(credentialsProvider); TextPage page = webClient.getPage(base + "protected/servlet"); assertTrue(page.getContent().contains("This is a protected servlet")); } @Test public void testGetWithIncorrectCredentials() throws Exception { DefaultCredentialsProvider credentialsProvider = new DefaultCredentialsProvider(); credentialsProvider.addCredentials("wrong", "incorrect".toCharArray()); webClient.setCredentialsProvider(credentialsProvider); try { webClient.getPage(base + "protected/servlet"); } catch (FailingHttpStatusCodeException e) { assertEquals(401, e.getStatusCode()); return; } fail("/protected/servlet should not be accessible, but was."); } } ================================================ FILE: test/micro/snoop/pom.xml ================================================ 4.0.0 cloud.piranha.test.micro project 25.4.0-SNAPSHOT piranha-test-micro-snoop war Piranha - Test - Micro - Snoop UTF-8 jakarta.servlet jakarta.servlet-api provided cloud.piranha.test test-common test org.htmlunit htmlunit test org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test snoop org.apache.maven.plugins maven-antrun-plugin post-integration-test run org.apache.maven.plugins maven-dependency-plugin copy pre-integration-test copy cloud.piranha.dist piranha-dist-micro ${project.version} true ${project.build.directory} piranha-dist-micro.jar ================================================ FILE: test/micro/snoop/src/main/java/snoop/SnoopServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package snoop; import java.io.IOException; import java.io.PrintWriter; import java.util.Arrays; import java.util.Enumeration; import java.util.Locale; import java.lang.System.Logger.Level; import java.lang.System.Logger; import jakarta.servlet.ServletException; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; /** * A simple Snoop Servlet. * * @author Manfred Riem (mriem@manorrock.com) */ public class SnoopServlet extends HttpServlet { /** * Stores the logger. */ private static final Logger LOGGER = System.getLogger(SnoopServlet.class.getName()); /** * Stores the '</table>' constant. */ private static final String TABLE_END = ""; /** * Stores the '<table>' constant. */ private static final String TABLE_START = ""; /** * Stores the serial version UID. */ private static final long serialVersionUID = 1L; /** * Processes requests for both HTTP GET and POST * methods. * * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); try ( PrintWriter out = response.getWriter()) { String template = """ Snoop

Snoop

"""; out.println(String.format(template, request.getAttributeNames(), request.getAuthType(), request.getCharacterEncoding(), request.getClass(), request.getContentLength(), request.getContentType(), request.getContextPath(), Arrays.toString(request.getCookies()), request.getDispatcherType(), request.getHeaderNames(), request.getLocalAddr() )); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); if (request.getContentType() != null && request.getContentType().contains("multipart/form-data")) { out.println(""); } out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(""); out.println(TABLE_END); out.println("Attributes"); out.println(collectRequestAttributes(request)); out.println("Cookies"); Cookie[] cookies = request.getCookies(); if (cookies != null) { out.println(TABLE_START); String cookieEntry = """ """; for (int i = 0; i < cookies.length; i++) { out.println(String.format(cookieEntry, cookies[i].getName(), cookies[i].getValue())); } out.println(TABLE_END); } out.println("Headers"); Enumeration headerNames = request.getHeaderNames(); out.println(TABLE_START); while (headerNames.hasMoreElements()) { String name = headerNames.nextElement(); out.println(""); } out.println(TABLE_END); out.println("Locales"); Enumeration locales = request.getLocales(); out.println(TABLE_START); while (locales.hasMoreElements()) { Locale locale = locales.nextElement(); out.println(""); } out.println(TABLE_END); out.println("Parameters"); Enumeration parameterNames = request.getParameterNames(); out.println(TABLE_START); while (parameterNames.hasMoreElements()) { String name = parameterNames.nextElement(); out.println(""); } out.println(TABLE_END); out.println("Session"); HttpSession session = request.getSession(true); String sessionTable = """
Attribute Names%s
Auth Type%s
Character Encoding%s
Class%s
Content Length%s
Content Type%s
Context Path%s
Cookies%s
Dispatchker Type%s
Header Names%s
Local Address%s
Local Name:" + request.getLocalName() + "
Local Port:" + request.getLocalPort() + "
Locale:" + request.getLocale() + "
Locales:" + request.getLocales() + "
Method:" + request.getMethod() + "
Parameter Map:" + request.getParameterMap() + "
Parameter Names:" + request.getParameterNames() + "
Parts:" + request.getParts() + "
Path Info:" + request.getPathInfo() + "
Path Translated:" + request.getPathTranslated() + "
Protocol:" + request.getProtocol() + "
Query String:" + request.getQueryString() + "
Remote Address:" + request.getRemoteAddr() + "
Remote Host:" + request.getRemoteHost() + "
Remote Port:" + request.getRemotePort() + "
Remote User:" + request.getRemoteUser() + "
Request URI:" + request.getRequestURI() + "
Request URL:" + request.getRequestURL() + "
Requested Session Id:" + request.getRequestedSessionId() + "
Scheme:" + request.getScheme() + "
Server Name:" + request.getServerName() + "
Server Port:" + request.getServerPort() + "
Servlet Context:" + request.getServletContext() + "
Servlet Path:" + request.getServletPath() + "
User Principal:" + request.getUserPrincipal() + "
Is Async Started:" + request.isAsyncStarted() + "
Is Async Supported:" + request.isAsyncSupported() + "
Is Requested Session Id From Cookie:" + request.isRequestedSessionIdFromCookie() + "
Is Requested Session Id From URL:" + request.isRequestedSessionIdFromURL() + "
Is Secure:" + request.isSecure() + "
%s%s
" + name + "" + request.getHeader(name) + "
" + locale + "
" + name + "" + Arrays.toString(request.getParameterValues(name)) + "
%s%s%s%s%s%s
Creation Time
Last Accessed Time
Session Context
Max Inactive Interval
Id
Is New
"""; out.println(String.format(sessionTable, session.getCreationTime(), session.getLastAccessedTime(), session.getServletContext(), session.getMaxInactiveInterval(), session.getId(), session.isNew() )); out.println("Session Attributes"); out.println(TABLE_START); session.setAttribute("TEST", "TEST"); Enumeration names = session.getAttributeNames(); while (names.hasMoreElements()) { String name = names.nextElement(); out.println("" + name + "" + session.getAttribute(name) + ""); } out.println(TABLE_END); out.println(""); out.println(""); } } /** * Collect the request attributes. * * @param request the request attributes. * @return the HTML for the request attributes. */ private String collectRequestAttributes(HttpServletRequest request) { Enumeration attributeNames = request.getAttributeNames(); StringBuilder result = new StringBuilder(); result.append(TABLE_START); while (attributeNames.hasMoreElements()) { String name = attributeNames.nextElement(); result.append("") .append(name) .append("") .append(request.getAttribute(name)) .append(""); } result.append(TABLE_END); return result.toString(); } /** * Handles the HTTP GET method. * * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { processRequest(request, response); } catch (ServletException | IOException e) { LOGGER.log(Level.ERROR, "Unable to process request", e); } } /** * Handles the HTTP POST method. * * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { processRequest(request, response); } catch (ServletException | IOException e) { LOGGER.log(Level.ERROR, "Unable to process request", e); } } } ================================================ FILE: test/micro/snoop/src/main/webapp/WEB-INF/beans.xml ================================================ ================================================ FILE: test/micro/snoop/src/main/webapp/WEB-INF/web.xml ================================================ SnoopServlet snoop.SnoopServlet SnoopServlet /Snoop 30 ================================================ FILE: test/micro/snoop/src/test/java/snoop/SnoopIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package snoop; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.time.Duration; import org.htmlunit.WebClient; import org.htmlunit.html.HtmlPage; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import cloud.piranha.test.common.PiranhaStartup; import me.alexpanov.net.FreePortFinder; /** * The integration tests for the Snoop web application. * * @author Manfred Riem (mriem@manorrock.com) */ public class SnoopIT { /** * Stores the port */ private static int port; /** * Stores the process. */ private static Process process; /** * Stores the web client. */ private WebClient webClient; /** * Cleanup after tests. */ @AfterAll public static void afterAll() { process.destroyForcibly(); } /** * Cleanup after each test. */ @AfterEach public void afterEach() { webClient.close(); } /** * Setup before all tests. * * @throws Exception when an error occurs. */ @BeforeAll public static void beforeAll() throws Exception { port = FreePortFinder.findFreeLocalPort(); process = new ProcessBuilder() .directory(new File("target")) .command("java", "-jar", "piranha-dist-micro.jar", "--http-port", String.valueOf(port), "--war-file", "snoop.war", "--verbose") .inheritIO() .start(); PiranhaStartup.waitUntilPiranhaReady(process, port); } /** * Setup before each test. */ @BeforeEach public void beforeEach() { webClient = new WebClient(); } /** * Test accessing Snoop servlet. * * @throws Exception when a serious error occurs. */ @Test public void testSnoop() throws Exception { Thread.sleep(Duration.ofSeconds(5)); HtmlPage page = webClient.getPage("http://localhost:" + port + "/Snoop"); assertTrue(page.asXml().contains("Snoop")); } } ================================================ FILE: test/pom.xml ================================================ 4.0.0 cloud.piranha project 25.4.0-SNAPSHOT cloud.piranha.test project pom Piranha - Test embedded common coreprofile jpms micro server servlet webprofile UTF-8 cloud.piranha bom ${project.version} pom import cloud.piranha.test test-common ${project.version} test org.apache.maven.plugins maven-javadoc-plugin true tck tck ================================================ FILE: test/server/helloworld/pom.xml ================================================ 4.0.0 cloud.piranha.test.server project 25.4.0-SNAPSHOT piranha-test-server-helloworld war Piranha - Test - Server - Hello World application server UTF-8 org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test helloworld org.codehaus.mojo build-helper-maven-plugin reserve-network-port reserve-network-port pre-integration-test httpPort cloud.piranha.maven piranha-maven-plugin ${project.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop server ${httpPort} 60 org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify 1 false 50 ${httpPort} org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false ================================================ FILE: test/server/helloworld/src/main/webapp/helloworld.html ================================================ Hello World
Hello World!
================================================ FILE: test/server/helloworld/src/test/java/helloworld/HelloWorldIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package helloworld; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; class HelloWorldIT { @Test void testHelloWorldHtml() throws Exception { Thread.sleep(3000); HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + System.getProperty("httpPort") + "/helloworld/helloworld.html")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello World!")); } } ================================================ FILE: test/server/pom.xml ================================================ 4.0.0 cloud.piranha.test project 25.4.0-SNAPSHOT cloud.piranha.test.server project pom Piranha - Test - Server - Project helloworld programmatic wasp wasp2 cloud.piranha.dist piranha-dist-server ${project.version} pom provided ================================================ FILE: test/server/programmatic/pom.xml ================================================ 4.0.0 cloud.piranha.test.server project 25.4.0-SNAPSHOT piranha-test-server-programmatic war Piranha - Test - Server - Programmatic UTF-8 jakarta.platform jakarta.jakartaee-web-api provided cloud.piranha.dist piranha-dist-server ${project.version} test org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test piranha-test-server-programmatic org.apache.maven.plugins maven-antrun-plugin pre-integration-test pre-integration-test run post-integration-test post-integration-test run org.apache.maven.plugins maven-failsafe-plugin 1 false org.apache.maven.plugins maven-war-plugin false ================================================ FILE: test/server/programmatic/src/main/java/programmatic/ProgrammaticServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package programmatic; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * The ProgrammaticServlet Servlet. * * @author Manfred Riem (mriem@manorrock.com) */ public class ProgrammaticServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); try ( PrintWriter out = response.getWriter()) { out.println(""" Programmatic Servlet

Programmatic Servlet!

"""); } } } ================================================ FILE: test/server/programmatic/src/main/webapp/WEB-INF/web.xml ================================================ ProgrammaticServlet programmatic.ProgrammaticServlet ProgrammaticServlet /index.html 30 ================================================ FILE: test/server/programmatic/src/test/java/programmatic/ProgrammaticIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package programmatic; import cloud.piranha.extension.servlet.ServletExtension; import cloud.piranha.multi.MultiPiranha; import cloud.piranha.multi.MultiPiranhaBuilder; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import org.junit.jupiter.api.AfterEach; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * The Piranha Server programmatic integration test. * * @author Manfred Riem (mriem@manorrock.com) */ class ProgrammaticIT { /** * Stores the Piranha Server instance. */ private MultiPiranha piranha; /** * After each test. */ @AfterEach void afterEach() { piranha.stop(); } /** * Before each test. */ @BeforeEach void beforeEach() { piranha = new MultiPiranhaBuilder() .extensionClass(ServletExtension.class) .httpPort(8200) .webAppsDir("target/webapps") .build(); piranha.start(); } /** * Test the 'Hello World!' servlet. * * @throws Exception when a serious error occurs. */ @Test void testHelloWorld() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:8200/index.html")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Programmatic Servlet!")); } } ================================================ FILE: test/server/wasp/pom.xml ================================================ 4.0.0 cloud.piranha.test.server project 25.4.0-SNAPSHOT piranha-test-server-wasp war Piranha - Test - Server - WaSP UTF-8 cloud.piranha.test.server piranha-test-server-wasp2 ${project.version} compile cloud.piranha.dist piranha-dist-server ${project.version} provided zip true jakarta.platform jakarta.jakartaee-web-api provided org.junit.jupiter junit-jupiter test piranha-test-server-wasp org.codehaus.mojo build-helper-maven-plugin reserve-network-port reserve-network-port pre-integration-test httpPort cloud.piranha.maven piranha-maven-plugin ${project.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop server ${httpPort} org.apache.maven.plugins maven-failsafe-plugin 1 false 100 ${httpPort} org.apache.maven.plugins maven-war-plugin false ================================================ FILE: test/server/wasp/src/main/java/wasp/HelloTldTag.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package wasp; import jakarta.servlet.jsp.JspException; import jakarta.servlet.jsp.tagext.TagSupport; import java.io.IOException; /** * A 'Hello TLD' tag. * * @author Manfred Riem (mriem@manorrock.com) */ public class HelloTldTag extends TagSupport { @Override public int doStartTag() throws JspException { try { pageContext.getOut().println("Hello TLD"); } catch (IOException ioe) { throw new JspException(ioe); } return SKIP_BODY; } } ================================================ FILE: test/server/wasp/src/main/webapp/WEB-INF/tags/hellotag.tag ================================================ <%@tag pageEncoding="UTF-8"%> Hello Tag ================================================ FILE: test/server/wasp/src/main/webapp/WEB-INF/tld/hellotag.tld ================================================ 1.0 hellotag /WEB-INF/tld/hellotag hellotag /WEB-INF/tags/hellotag.tag ================================================ FILE: test/server/wasp/src/main/webapp/WEB-INF/tld/hellotld.tld ================================================ 1.0 hellotld /WEB-INF/tld/hellotld hellotld wasp.HelloTldTag JSP ================================================ FILE: test/server/wasp/src/main/webapp/WEB-INF/web.xml ================================================ /WEB-INF/tld/hellotld.tld http://hello.tld ================================================ FILE: test/server/wasp/src/main/webapp/hellotag.jsp ================================================ <%@page contentType="text/html" pageEncoding="UTF-8"%> <%@taglib prefix="hellotag" uri="/WEB-INF/tld/hellotag"%> Testing tag coming from tag file ================================================ FILE: test/server/wasp/src/main/webapp/hellotag2.jsp ================================================ <%@page contentType="text/html" pageEncoding="UTF-8"%> <%@taglib prefix="hellotag2" uri="/META-INF/hellotag2"%> Testing tag coming from a tag file in a JAR ================================================ FILE: test/server/wasp/src/main/webapp/hellotld.jsp ================================================ <%@page contentType="text/html" pageEncoding="UTF-8"%> <%@taglib prefix="hellotld" uri="/WEB-INF/tld/hellotld"%> Testing tag configured in TLD descriptor ================================================ FILE: test/server/wasp/src/main/webapp/hellotld2.jsp ================================================ <%@page contentType="text/html" pageEncoding="UTF-8"%> <%@taglib prefix="hellotld2" uri="http://hello.tld"%> Testing tag configured in web.xml ================================================ FILE: test/server/wasp/src/main/webapp/index.jsp ================================================ <%@page contentType="text/html" pageEncoding="UTF-8"%> Index page

This web application contains the following tests:

  1. Testing the <hello-tld> tag using taglib config
  2. Testing the <hello-tld> tag using web.xml config
================================================ FILE: test/server/wasp/src/test/java/wasp/HelloTagJspIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package wasp; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The integration tests for the hellotag.jsp page. * * @author Manfred Riem (mriem@manorrock.com) */ class HelloTagJspIT { /** * Test the hellotag.jsp. * * @throws Exception when a serious error occurs. */ @Test void testHelloTagJsp() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + System.getProperty("httpPort") + "/piranha-test-server-wasp/hellotag.jsp")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello Tag")); } } ================================================ FILE: test/server/wasp/src/test/java/wasp/HelloTld2JspIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package wasp; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The integration tests for the hellotld2.jsp page. * * @author Manfred Riem (mriem@manorrock.com) */ class HelloTld2JspIT { /** * Test the index.jsp. * * @throws Exception when a serious error occurs. */ @Test void testHelloTld2Jsp() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + System.getProperty("httpPort") + "/piranha-test-server-wasp/hellotld2.jsp")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello TLD")); } } ================================================ FILE: test/server/wasp/src/test/java/wasp/HelloTldJspIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package wasp; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The integration tests for the hellotld.jsp page. * * @author Manfred Riem (mriem@manorrock.com) */ class HelloTldJspIT { /** * Test the index.jsp. * * @throws Exception when a serious error occurs. */ @Test void testHelloTldJsp() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + System.getProperty("httpPort") + "/piranha-test-server-wasp/hellotld.jsp")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello TLD")); } } ================================================ FILE: test/server/wasp/src/test/java/wasp/IndexJspIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package wasp; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The integration tests for the index.jsp page. * * @author Manfred Riem (mriem@manorrock.com) */ class IndexJspIT { /** * Test the index.jsp. * * @throws Exception when a serious error occurs. */ @Test void testIndexJsp() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + System.getProperty("httpPort") + "/piranha-test-server-wasp/index.jsp")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("This web application contains the following tests:")); assertFalse(response.body().contains("<%@page contentType=\"text/html\" pageEncoding=\"UTF-8\"%>")); } /** * Test the index.jsp (by means of implicit welcome-file). * * @throws Exception when a serious error occurs. */ @Test void testIndexJsp2() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + System.getProperty("httpPort") + "/piranha-test-server-wasp/")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("This web application contains the following tests:")); assertFalse(response.body().contains("<%@page contentType=\"text/html\" pageEncoding=\"UTF-8\"%>")); } } ================================================ FILE: test/server/wasp2/pom.xml ================================================ 4.0.0 cloud.piranha.test.server project 25.4.0-SNAPSHOT piranha-test-server-wasp2 jar Piranha - Test - Server - WaSP #2 UTF-8 jakarta.platform jakarta.jakartaee-web-api provided ================================================ FILE: test/server/wasp2/src/main/resources/META-INF/hellotag2.tag ================================================ <%@tag pageEncoding="UTF-8"%> Hello Tag #2 ================================================ FILE: test/server/wasp2/src/main/resources/META-INF/hellotag2.tld ================================================ 1.0 hellotag2 /META-INF/hellotag2 hellotag2 hellotag2.tag ================================================ FILE: test/servlet/crac/pom.xml ================================================ 4.0.0 cloud.piranha.test.servlet project 25.4.0-SNAPSHOT piranha-test-servlet-crac war Piranha - Test - Servlet - CRaC application servlet UTF-8 jakarta.enterprise jakarta.enterprise.cdi-api provided org.glassfish jakarta.faces runtime cloud.piranha.extension piranha-extension-weld ${project.version} runtime org.jboss.weld.servlet weld-servlet-shaded ${weld.version} runtime org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test crac unix unix cloud.piranha.maven piranha-maven-plugin ${project.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop servlet ${httpPort} org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin integration-test verify 1 ${httpPort} org.apache.maven.plugins maven-war-plugin false org.codehaus.mojo build-helper-maven-plugin reserve-network-port reserve-network-port validate httpPort ================================================ FILE: test/servlet/crac/src/main/java/cloud/piranha/test/servlet/crac/CracBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.servlet.crac; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Named; @Named(value = "cracBean") @RequestScoped public class CracBean { /** * Stores the 'Hello from CRaC!' message. */ private String hello = "Hello from CRaC!"; /** * Get the message. * * @return the message. */ public String getHello() { return hello; } /** * Set the message. * * @param hello the message. */ public void setHello(String hello) { this.hello = hello; } } ================================================ FILE: test/servlet/crac/src/main/webapp/WEB-INF/beans.xml ================================================ ================================================ FILE: test/servlet/crac/src/main/webapp/WEB-INF/web.xml ================================================ Faces Servlet jakarta.faces.webapp.FacesServlet 1 Faces Servlet *.xhtml ================================================ FILE: test/servlet/crac/src/main/webapp/crac.xhtml ================================================ Jakarta Faces application
================================================ FILE: test/servlet/crac/src/test/java/cloud/piranha/test/servlet/crac/CracIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.servlet.crac; import java.net.URI; import java.net.http.HttpClient; import static java.net.http.HttpClient.Redirect.ALWAYS; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import java.time.Duration; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; public class CracIT { private String portNumber = System.getProperty("httpPort"); @Disabled @Test public void testHelloXhtml() throws Exception { HttpClient client = HttpClient .newBuilder() .connectTimeout(Duration.ofSeconds(60)) .followRedirects(ALWAYS) .build(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + portNumber + "/crac/hello.xhtml")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello from CRaC!")); } } ================================================ FILE: test/servlet/hello/pom.xml ================================================ 4.0.0 cloud.piranha.test.servlet project 25.4.0-SNAPSHOT piranha-test-servlet-hello war Piranha - Test - Servlet - Hello servlet UTF-8 jakarta.servlet jakarta.servlet-api provided org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test piranha-test-servlet-hello org.apache.maven.plugins maven-war-plugin false ================================================ FILE: test/servlet/hello/src/main/java/hello/HelloServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package hello; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * The HelloServlet Servlet. * * @author Manfred Riem (mriem@manorrock.com) */ public class HelloServlet extends HttpServlet { /** * Process the GET request. * * @param request the request. * @param response the response. * @throws ServletException when a Servlet error occurs. * @throws IOException when an I/O error occurs. */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); try ( PrintWriter out = response.getWriter()) { out.println(""" Hello Servlet!

Hello Servlet!

"""); } } } ================================================ FILE: test/servlet/hello/src/main/webapp/WEB-INF/web.xml ================================================ HelloServlet hello.HelloServlet HelloServlet /helloservlet 30 ================================================ FILE: test/servlet/hello/src/main/webapp/helloel.jsp ================================================ <%@page contentType="text/html" pageEncoding="UTF-8"%> Hello EL

Hello ${param['name']}!

================================================ FILE: test/servlet/hello/src/main/webapp/hellojsp.jsp ================================================ <%@page contentType="text/html" pageEncoding="UTF-8"%> Hello JSP

Hello JSP!

================================================ FILE: test/servlet/hello/src/main/webapp/helloworld.html ================================================ Hello World
Hello World!
================================================ FILE: test/servlet/hello/src/test/java/hello/HelloIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package hello; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** * The Hello integration tests. * *
    *
  1. testHelloElJsp validates Jakarta Expression Language works
  2. *
  3. testHelloJspJsp validates Jakarta Pages works
  4. *
  5. testHelloServlet validates Jakarta Servlet works
  6. *
* * @author Manfred Riem (mriem@manorrock.com) */ class HelloIT { /** * Test helloworld.html. * * @throws Exception when a serious error occurs. */ @Disabled @Test void testHelloWorldHtml() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:9090/piranha-test-servlet-hello/helloworld.html")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello World!")); } /** * Test helloel.jsp. * * @throws Exception when a serious error occurs. */ @Disabled @Test void testHelloElJsp() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:9090/piranha-test-servlet-hello/helloel.jsp?name=EL")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello EL!")); } /** * Test hellojsp.jsp. * * @throws Exception when a serious error occurs. */ @Disabled @Test void testHelloJspJsp() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:9090/piranha-test-servlet-hello/hellojsp.jsp")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello JSP!")); } /** * Test HelloServlet Servlet. * * @throws Exception when a serious error occurs. */ @Disabled @Test void testHelloServlet() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:9090/piranha-test-servlet-hello/helloservlet")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello Servlet!")); } } ================================================ FILE: test/servlet/helloworld/pom.xml ================================================ 4.0.0 cloud.piranha.test.servlet project 25.4.0-SNAPSHOT piranha-test-servlet-helloworld war Piranha - Test - Servlet - HelloWorld web application servlet UTF-8 org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test piranha-test-servlet-helloworld org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} org.apache.maven.plugins maven-failsafe-plugin ${maven-failsafe-plugin.version} integration-test verify ${httpPort} org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false org.codehaus.mojo build-helper-maven-plugin reserve-network-port reserve-network-port validate httpPort ================================================ FILE: test/servlet/helloworld/src/main/webapp/helloworld.html ================================================ Hello World
Hello World!
================================================ FILE: test/servlet/helloworld/src/test/java/helloworld/HelloWorldIT.java ================================================ package helloworld; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; class HelloWorldIT { @Disabled @Test void testHelloWorldHtml() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + Integer.valueOf(System.getProperty("httpPort")) + "/helloworld/helloworld.html")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello World!")); } } ================================================ FILE: test/servlet/integration/pom.xml ================================================ 4.0.0 cloud.piranha.test.servlet project 25.4.0-SNAPSHOT piranha-test-servlet-integration war Piranha - Test - Servlet - Distribution Integration Tests UTF-8 jakarta.servlet jakarta.servlet-api provided org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test integration org.codehaus.mojo build-helper-maven-plugin reserve-network-port reserve-network-port validate httpPort cloud.piranha.maven piranha-maven-plugin ${project.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop servlet ${httpPort} org.apache.maven.plugins maven-failsafe-plugin integration-test verify 10 ${httpPort} org.apache.maven.plugins maven-war-plugin false ================================================ FILE: test/servlet/integration/src/main/java/cloud/piranha/test/servlet/integration/IntegrationServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.servlet.integration; import java.io.IOException; import java.io.PrintWriter; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * The Servlet testing Jakarta Servlet integration. * * @author Manfred Riem (mriem@manorrock.com) */ @WebServlet(name = "IntegrationServlet", urlPatterns = {"/servlet"}) public class IntegrationServlet extends HttpServlet { /** * Handles the HTTP GET method. * * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); try (PrintWriter out = response.getWriter()) { out.println(""); out.println(""); out.println(""); out.println("Servlet works!"); out.println(""); out.println(""); out.println("

Servlet works!

"); out.println(""); out.println(""); } } } ================================================ FILE: test/servlet/integration/src/test/java/cloud/piranha/test/servlet/integration/IntegrationtIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.test.servlet.integration; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * The integration tests validating the various integrations. * *
    *
  1. testServletIntegration validates Servlets works
  2. *
* * @author Manfred Riem (mriem@manorrock.com) */ class IntegrationtIT { /** * Stores the base URL. */ private final String baseUrl = "http://localhost:" + System.getProperty("httpPort") + "/integration"; /** * Test Servlet integration. */ @Test void testServletIntegration() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI(baseUrl + "/servlet")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Servlet works!")); } } ================================================ FILE: test/servlet/pages/README.md ================================================ # Create a Jakarta Pages application on Piranha Servlet See [Create a Jakarta Pages application on Piranha Servlet](https://piranha.cloud/servlet/guides/pages) for the step by step guide. This repository contains the resulting project for your reference. ================================================ FILE: test/servlet/pages/pom.xml ================================================ 4.0.0 cloud.piranha.test.servlet project 25.4.0-SNAPSHOT pages war Piranha - Test - Servlet - Pages application 21 servlet UTF-8 org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test pages cloud.piranha.maven piranha-maven-plugin ${project.version} pre-integration-test pre-integration-test start post-integration-test post-integration-test stop servlet ${httpPort} org.apache.maven.plugins maven-compiler-plugin ${java.version} org.apache.maven.plugins maven-failsafe-plugin integration-test verify 1 ${httpPort} org.apache.maven.plugins maven-war-plugin ${maven-war-plugin.version} false org.codehaus.mojo build-helper-maven-plugin reserve-network-port reserve-network-port package httpPort ================================================ FILE: test/servlet/pages/src/main/webapp/hello.jsp ================================================ <%@page contentType="text/html" pageEncoding="UTF-8"%>

Hello from Jakarta Pages!

================================================ FILE: test/servlet/pages/src/test/java/hello/HelloIT.java ================================================ package hello; import java.net.URI; import java.net.http.HttpClient; import static java.net.http.HttpClient.Redirect.ALWAYS; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import java.time.Duration; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; public class HelloIT { private final String portNumber = System.getProperty("httpPort"); @Test public void testPagesHelloJsp() throws Exception { HttpClient client = HttpClient .newBuilder() .connectTimeout(Duration.ofSeconds(60)) .followRedirects(ALWAYS) .build(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:" + portNumber + "/pages/hello.jsp")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello from Jakarta Pages!")); } } ================================================ FILE: test/servlet/pom.xml ================================================ 4.0.0 cloud.piranha.test project 25.4.0-SNAPSHOT cloud.piranha.test.servlet project pom Piranha - Test - Servlet - Project cloud.piranha.dist piranha-dist-servlet ${project.version} pom provided crac hello helloworld integration pages websocket ================================================ FILE: test/servlet/websocket/pom.xml ================================================ 4.0.0 cloud.piranha.test.servlet project 25.4.0-SNAPSHOT websocket war Piranha - Test - Servlet - WebSocket application 21 servlet UTF-8 jakarta.websocket jakarta.websocket-api provided jakarta.websocket jakarta.websocket-client-api provided websocket org.apache.maven.plugins maven-compiler-plugin ${java.version} org.apache.maven.plugins maven-war-plugin false ================================================ FILE: test/servlet/websocket/src/main/java/chat/ChatEndpoint.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package chat; import jakarta.websocket.OnMessage; import jakarta.websocket.Session; import jakarta.websocket.server.ServerEndpoint; @ServerEndpoint("/chat") public class ChatEndpoint { /** * Handle the message coming in. * * @param message the message. * @param session the session. * @return the response. */ @OnMessage public String onMessage(String message, Session session) { return message; } } ================================================ FILE: test/tck/coreprofile/annotations/installer/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile.annotations project 25.4.0-SNAPSHOT installer pom Piranha - Test - TCK - Core Profile - Annotations TCK - Installer org.apache.maven.plugins maven-antrun-plugin pre-integration-test pre-integration-test run ================================================ FILE: test/tck/coreprofile/annotations/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile project 25.4.0-SNAPSHOT cloud.piranha.test.tck.coreprofile.annotations project pom Piranha - Test - TCK - Core Profile - Annotations TCK jakarta.annotation jakarta-annotations-tck ${annotations.tck.version} installer runner ================================================ FILE: test/tck/coreprofile/annotations/runner/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile.annotations project 25.4.0-SNAPSHOT runner pom Piranha - Test - TCK - Core Profile - Annotations TCK - Runner ${project.build.directory}/jimage cloud.piranha.test.tck.coreprofile.annotations installer ${project.version} pom jakarta.annotation jakarta-annotations-tck test jakarta.tck sigtest-maven-plugin test org.junit.jupiter junit-jupiter test org.apache.maven.plugins maven-dependency-plugin pre-integration-test pre-integration-test copy jakarta.annotation jakarta.annotation-api ${project.build.directory} jakarta.annotation-api.jar org.apache.maven.plugins maven-failsafe-plugin sig-test integration-test verify jakarta.annotation:jakarta-annotations-tck ${jimage.dir} ${project.build.directory}/jakarta.annotation-api.jar:${jimage.dir}/java.base:${jimage.dir}/java.rmi:${jimage.dir}/java.sql:${jimage.dir}/java.naming true true true true true true true true true true true true true ================================================ FILE: test/tck/coreprofile/cdi/installer/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile.cdi project 25.4.0-SNAPSHOT installer pom Piranha - Test - TCK - Core Profile - CDI TCK - Installer org.apache.maven.plugins maven-antrun-plugin pre-integration-test pre-integration-test run ================================================ FILE: test/tck/coreprofile/cdi/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile project 25.4.0-SNAPSHOT cloud.piranha.test.tck.coreprofile.cdi project pom Piranha - Test - TCK - Core Profile - CDI TCK - Project installer runner ================================================ FILE: test/tck/coreprofile/cdi/runner/core/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile.cdi.runner project 25.4.0-SNAPSHOT core Piranha - Test - TCK - Core Profile - CDI TCK - Runner - Core 7.11.0 org.jboss.arquillian arquillian-bom ${arquillian.version} pom import jakarta.enterprise jakarta.enterprise.cdi-api provided jakarta.servlet jakarta.servlet-api test org.jboss.weld weld-core-impl ${weld.version} provided org.testng testng jakarta.el jakarta.el-api org.jboss.weld weld-lite-extension-translator ${weld.version} org.jboss.weld.module weld-jsf ${weld.version} org.testng testng org.jboss.weld.module weld-ejb ${weld.version} org.jboss.weld.module weld-web ${weld.version} jakarta.enterprise cdi-tck-api ${cdi.tck.version} test * * jakarta.enterprise cdi-tck-core-impl ${cdi.tck.version} test * * jakarta.enterprise cdi-tck-core-impl ${cdi.tck.version} xml suite test * * org.glassfish.expressly expressly provided * * commons-lang commons-lang 2.6 test org.testng testng ${testng.version} test org.jboss.arquillian.testng arquillian-testng-container test org.jboss.shrinkwrap.descriptors shrinkwrap-descriptors-impl-javaee 2.0.0 test cloud.piranha.arquillian piranha-arquillian-managed ${project.version} test org.apache.maven.plugins maven-compiler-plugin 21 -Xlint:unchecked maven-dependency-plugin copy-test-suites generate-test-sources copy jakarta.enterprise cdi-tck-core-impl ${cdi.tck.version} xml suite false tck-core-suite.xml target/suites false true org.apache.maven.plugins maven-jar-plugin process-test-classes test-jar ${project.build.directory}/dependency/lib false org.apache.maven.plugins maven-surefire-plugin test target/suites/tck-core-suite.xml cdi-full,integration,javaee-full,se jakarta.enterprise:cdi-tck-core-impl 1 surefire.testng.verbose 1 false -ea ${project.build.directory}/dependency/lib true org.apache.maven.plugins maven-surefire-report-plugin generate-test-report test report-only target/surefire-reports test-report ================================================ FILE: test/tck/coreprofile/cdi/runner/core/src/test/java/org/jboss/weld/tck/piranha/PiranhaDeploymentExceptionTransformer.java ================================================ /* * Copyright (c) 2024 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package org.jboss.weld.tck.piranha; import jakarta.enterprise.inject.spi.DefinitionException; import jakarta.enterprise.inject.spi.DeploymentException; import java.util.List; import org.apache.commons.lang.exception.ExceptionUtils; import org.jboss.arquillian.container.spi.client.container.DeploymentExceptionTransformer; /** * * See AS7-1197 for more details. * * @see org.jboss.weld.tck.glassfish.GlassFishExtension * @author J J Snyder (j.j.snyder@oracle.com) * @author Arjan Tijms */ public class PiranhaDeploymentExceptionTransformer implements DeploymentExceptionTransformer { private static final String[] DEPLOYMENT_EXCEPTION_FRAGMENTS = new String[] { "Only normal scopes can be passivating", "org.jboss.weld.exceptions.DeploymentException", "org.jboss.weld.exceptions.UnserializableDependencyException", "org.jboss.weld.exceptions.InconsistentSpecializationException", "CDI deployment failure:", "org.jboss.weld.exceptions.NullableDependencyException" }; private static final String[] DEFINITION_EXCEPTION_FRAGMENTS = new String[] { "CDI definition failure:", "org.jboss.weld.exceptions.DefinitionException" }; @Override public Throwable transform(Throwable throwable) { @SuppressWarnings("unchecked") List throwableList = ExceptionUtils.getThrowableList(throwable); if (throwableList.size() < 1) return throwable; Throwable root = null; if (throwableList.size() == 1) { root = throwable; } else { root = ExceptionUtils.getRootCause(throwable); } if (root instanceof DeploymentException || root instanceof DefinitionException) { return root; } if (isFragmentFound(DEPLOYMENT_EXCEPTION_FRAGMENTS, root)) { return new DeploymentException(root.getMessage()); } if (isFragmentFound(DEFINITION_EXCEPTION_FRAGMENTS, root)) { return new DefinitionException(root.getMessage()); } return throwable; } private boolean isFragmentFound(String[] fragments, Throwable rootException) { for (String fragment : fragments) { if (rootException.getMessage().contains(fragment)) { return true; } } return false; } } ================================================ FILE: test/tck/coreprofile/cdi/runner/core/src/test/java/org/jboss/weld/tck/piranha/PiranhaExtension.java ================================================ /* * Copyright (c) 2002-2024 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package org.jboss.weld.tck.piranha; import org.jboss.arquillian.container.spi.client.container.DeploymentExceptionTransformer; import org.jboss.arquillian.core.spi.LoadableExtension; /** * Registers the exception transformer to properly identify deployment failures. * */ public class PiranhaExtension implements LoadableExtension { @Override public void register(ExtensionBuilder builder) { builder.service(DeploymentExceptionTransformer.class, PiranhaDeploymentExceptionTransformer.class); } } ================================================ FILE: test/tck/coreprofile/cdi/runner/core/src/test/java/org/jboss/weld/tck/piranha/WeldBeansImpl.java ================================================ /* * Copyright (c) 2024 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package org.jboss.weld.tck.piranha; import java.io.IOException; import java.util.Arrays; import org.jboss.cdi.tck.spi.Beans; /** * CDI TCK tests use this class as an adapter between the test application and server container. * * Then its implementation can simplify the behavior, ie. explicit passivation, while * in a real application the decision to passivate/activate some object is on the container * and cannot be requested by the application. *

* Until GlassFish provides standalone utility to do that, we have to fake * the passivation/activation. * * @author David Matejcek */ public class WeldBeansImpl implements Beans { private Object fakeSerialized; @Override public boolean isProxy(Object instance) { return instance.getClass().getName().indexOf("_$$_Weld") > 0; } @Override public byte[] passivate(Object instance) throws IOException { fakeSerialized = instance; return instance.toString().getBytes(); } @Override public Object activate(byte[] bytes) throws IOException, ClassNotFoundException { if (Arrays.equals(fakeSerialized.toString().getBytes(), bytes)) { Object result = fakeSerialized; fakeSerialized = null; return result; } return null; } } ================================================ FILE: test/tck/coreprofile/cdi/runner/core/src/test/java/org/jboss/weld/tck/piranha/WeldContextImpl.java ================================================ /* * Copyright (c) 2024 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package org.jboss.weld.tck.piranha; import jakarta.enterprise.context.spi.Context; import jakarta.enterprise.inject.spi.CDI; import jakarta.servlet.ServletContext; import org.jboss.cdi.tck.spi.Contexts; import org.jboss.weld.Container; import org.jboss.weld.context.ApplicationContext; import org.jboss.weld.context.DependentContext; import org.jboss.weld.context.ManagedContext; import org.jboss.weld.context.RequestContext; import org.jboss.weld.context.http.HttpRequestContext; import org.jboss.weld.util.ForwardingContext; public class WeldContextImpl implements Contexts { @Override public void setActive(Context context) { context = ForwardingContext.unwrap(context); if (context instanceof ManagedContext managedContext) { managedContext.activate(); } else if (context instanceof ApplicationContext) { // No-op, always active } else { throw new UnsupportedOperationException(); } } @Override public void setInactive(Context context) { context = ForwardingContext.unwrap(context); if (context instanceof ManagedContext managedContext) { managedContext.deactivate(); } else { throw new UnsupportedOperationException(); } } @Override public RequestContext getRequestContext() { return Container.instance(getContextId()) .deploymentManager() .instance() .select(HttpRequestContext.class) .get(); } @Override public DependentContext getDependentContext() { return Container.instance(getContextId()) .deploymentManager().instance() .select(DependentContext.class) .get(); } @Override public void destroyContext(Context context) { context = ForwardingContext.unwrap(context); if (context instanceof ManagedContext managedContext) { managedContext.invalidate(); managedContext.deactivate(); managedContext.activate(); } else if (context instanceof ApplicationContext applicationContext) { applicationContext.invalidate(); } else { throw new UnsupportedOperationException(); } } private String getContextId() { ServletContext servletContext = CDI.current().select(ServletContext.class).get(); return servletContext.getInitParameter("WELD_CONTEXT_ID_KEY"); } } ================================================ FILE: test/tck/coreprofile/cdi/runner/core/src/test/java/org/jboss/weld/tck/piranha/WeldELImpl.java ================================================ /* * Copyright (c) 2024 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package org.jboss.weld.tck.piranha; import jakarta.el.ArrayELResolver; import jakarta.el.BeanELResolver; import jakarta.el.CompositeELResolver; import jakarta.el.ELContext; import jakarta.el.ELContextEvent; import jakarta.el.ELContextListener; import jakarta.el.ELResolver; import jakarta.el.ExpressionFactory; import jakarta.el.FunctionMapper; import jakarta.el.ListELResolver; import jakarta.el.MapELResolver; import jakarta.el.ResourceBundleELResolver; import jakarta.el.VariableMapper; import jakarta.enterprise.inject.spi.BeanManager; import org.jboss.cdi.tck.spi.EL; import org.jboss.weld.bean.builtin.BeanManagerProxy; import org.jboss.weld.manager.BeanManagerImpl; import org.jboss.weld.module.web.el.WeldELContextListener; import org.jboss.weld.module.web.el.WeldExpressionFactory; public class WeldELImpl implements EL { private static final ExpressionFactory EXPRESSION_FACTORY = new WeldExpressionFactory(ExpressionFactory.newInstance()); private static final ELContextListener[] EL_CONTEXT_LISTENERS = { new WeldELContextListener() }; @Override @SuppressWarnings("unchecked") public T evaluateValueExpression(BeanManager beanManager, String expression, Class expectedType) { ELContext elContext = createELContext(beanManager); return (T) EXPRESSION_FACTORY.createValueExpression(elContext, expression, expectedType).getValue(elContext); } @Override @SuppressWarnings("unchecked") public T evaluateMethodExpression(BeanManager beanManager, String expression, Class expectedType, Class[] expectedParamTypes, Object[] expectedParams) { ELContext elContext = createELContext(beanManager); return (T) EXPRESSION_FACTORY.createMethodExpression(elContext, expression, expectedType, expectedParamTypes).invoke( elContext, expectedParams); } @Override public ELContext createELContext(BeanManager beanManager) { if (beanManager instanceof BeanManagerProxy) { BeanManagerProxy proxy = (BeanManagerProxy) beanManager; beanManager = proxy.delegate(); } if (beanManager instanceof BeanManagerImpl) { return createELContext((BeanManagerImpl) beanManager); } throw new IllegalStateException("Wrong manager"); } private ELContext createELContext(BeanManagerImpl beanManagerImpl) { final ELResolver resolver = createELResolver(beanManagerImpl); ELContext context = new ELContext() { @Override public ELResolver getELResolver() { return resolver; } @Override public FunctionMapper getFunctionMapper() { return null; } @Override public VariableMapper getVariableMapper() { return null; } }; callELContextListeners(context); return context; } private ELResolver createELResolver(BeanManagerImpl beanManagerImpl) { CompositeELResolver resolver = new CompositeELResolver(); resolver.add(beanManagerImpl.getELResolver()); resolver.add(new MapELResolver()); resolver.add(new ListELResolver()); resolver.add(new ArrayELResolver()); resolver.add(new ResourceBundleELResolver()); resolver.add(new BeanELResolver()); return resolver; } private void callELContextListeners(ELContext context) { ELContextEvent event = new ELContextEvent(context); for (ELContextListener listener : EL_CONTEXT_LISTENERS) { listener.contextCreated(event); } } } ================================================ FILE: test/tck/coreprofile/cdi/runner/core/src/test/resources/META-INF/cdi-tck.properties ================================================ org.jboss.cdi.tck.cdiLiteMode=true org.jboss.cdi.tck.spi.Beans=org.jboss.weld.tck.piranha.WeldBeansImpl org.jboss.cdi.tck.spi.Contexts=org.jboss.weld.tck.piranha.WeldContextImpl org.jboss.cdi.tck.spi.EL=org.jboss.weld.tck.piranha.WeldELImpl ================================================ FILE: test/tck/coreprofile/cdi/runner/core/src/test/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension ================================================ org.jboss.weld.tck.piranha.PiranhaExtension ================================================ FILE: test/tck/coreprofile/cdi/runner/core/src/test/resources/arquillian.xml ================================================ ================================================ FILE: test/tck/coreprofile/cdi/runner/model/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile.cdi.runner project 25.4.0-SNAPSHOT model Piranha - Test - TCK - Core Profile - CDI TCK - Runner - Lang Model Aggregates dependencies and runs the CDI Lang Model TCK on Piranha org.jboss.arquillian arquillian-bom ${arquillian.version} pom import jakarta.enterprise jakarta.enterprise.cdi-api provided org.jboss.arquillian.junit5 arquillian-junit5-container test jakarta.enterprise cdi-tck-lang-model ${cdi.tck.version} test org.junit.jupiter junit-jupiter-engine test cloud.piranha.arquillian piranha-arquillian-managed ${project.version} test maven-compiler-plugin -proc:none maven-jar-plugin test-jar ${project.build.directory}/dependency/lib false maven-surefire-plugin -Xmx768m 1 true -ea CDILangModelTCKRunner.java maven-surefire-report-plugin generate-test-report verify report-only ${project.build.directory}/surefire-reports test-report ================================================ FILE: test/tck/coreprofile/cdi/runner/model/src/test/java/org/glassfish/tck/cdi/lang/model/CDILangModelTCKRunner.java ================================================ /* * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at * http://www.eclipse.org/legal/epl-2.0. * * This Source Code may also be made available under the following Secondary * Licenses when the conditions for such availability set forth in the * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, * version 2 with the GNU Classpath Exception, which is available at * https://www.gnu.org/software/classpath/license.html. * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 */ package org.glassfish.tck.cdi.lang.model; import jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit5.ArquillianExtension; import org.jboss.cdi.lang.model.tck.LangModelVerifier; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.EmptyAsset; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import static java.lang.System.Logger.Level.INFO; import static org.glassfish.tck.cdi.lang.model.LangModelVerifierBuildCompatibleExtension.langModelVerifierBuildCompatibleExtensionPassed; import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(ArquillianExtension.class) public class CDILangModelTCKRunner { private static final System.Logger LOG = System.getLogger(CDILangModelTCKRunner.class.getName()); @Deployment public static WebArchive deploy() { WebArchive archive = ShrinkWrap.create(WebArchive.class) // The package we are testing .addPackage(LangModelVerifier.class.getPackage()) // The build compatible extension that starts the test .addClass(LangModelVerifierBuildCompatibleExtension.class) .addAsServiceProvider(BuildCompatibleExtension.class, LangModelVerifierBuildCompatibleExtension.class) .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml"); LOG.log(INFO, archive.toString(true)); return archive; } @Test public void testCDILangModel() { // If the extension has been called, and no exception was thrown assertTrue(langModelVerifierBuildCompatibleExtensionPassed); } } ================================================ FILE: test/tck/coreprofile/cdi/runner/model/src/test/java/org/glassfish/tck/cdi/lang/model/LangModelVerifierBuildCompatibleExtension.java ================================================ /* * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at * http://www.eclipse.org/legal/epl-2.0. * * This Source Code may also be made available under the following Secondary * Licenses when the conditions for such availability set forth in the * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, * version 2 with the GNU Classpath Exception, which is available at * https://www.gnu.org/software/classpath/license.html. * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 */ package org.glassfish.tck.cdi.lang.model; import jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension; import jakarta.enterprise.inject.build.compatible.spi.ClassConfig; import jakarta.enterprise.inject.build.compatible.spi.Discovery; import jakarta.enterprise.inject.build.compatible.spi.Enhancement; import jakarta.enterprise.inject.build.compatible.spi.ScannedClasses; import org.jboss.cdi.lang.model.tck.LangModelVerifier; public class LangModelVerifierBuildCompatibleExtension implements BuildCompatibleExtension { public static boolean langModelVerifierBuildCompatibleExtensionPassed; @Discovery public void discovery(ScannedClasses scannedClasses) { scannedClasses.add(LangModelVerifier.class.getName()); } @Enhancement(types = LangModelVerifier.class) public void configure(ClassConfig classConfig) { LangModelVerifier.verify(classConfig.info()); // If there's an error, the verify() method will throw an exception langModelVerifierBuildCompatibleExtensionPassed = true; } } ================================================ FILE: test/tck/coreprofile/cdi/runner/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile.cdi project 25.4.0-SNAPSHOT cloud.piranha.test.tck.coreprofile.cdi.runner project pom Piranha - Test - TCK - Core Profile - CDI TCK - Runner - Project core model signature ================================================ FILE: test/tck/coreprofile/cdi/runner/signature/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile.cdi.runner project 25.4.0-SNAPSHOT signature Piranha - Test - TCK - Core Profile - CDI TCK - Runner - Signature Aggregates dependencies and runs the CDI Signature TCK on Piranha ${project.build.directory}/piranha org.apache.maven.plugins maven-dependency-plugin copy-cdi-sigtest process-resources copy jakarta.enterprise cdi-tck-core-impl ${cdi.tck.version} sig sigtest-jdk17 ${project.build.directory}/sigtest true true unpack-piranha process-test-resources unpack ${piranha.root}/dependency-maven-plugin-markers cloud.piranha.dist piranha-dist-coreprofile ${project.version} jar false target/classes jakarta.tck sigtest-maven-plugin sigtest verify check target/sigtest/cdi-tck-core-impl-sigtest-jdk17.sig jakarta.decorator,jakarta.enterprise.**,jakarta.interceptor target/classes target/cdi-sig-report.txt ================================================ FILE: test/tck/coreprofile/coreprofile/installer/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile.coreprofile project 25.4.0-SNAPSHOT installer pom Piranha - Test - TCK - Core Profile - Core Profile TCK - Installer ${project.build.directory}/tck org.apache.maven.plugins maven-antrun-plugin validate validate run ================================================ FILE: test/tck/coreprofile/coreprofile/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile project 25.4.0-SNAPSHOT cloud.piranha.test.tck.coreprofile.coreprofile project pom Piranha - Test - TCK - Core Profile - Core Profile TCK - Project installer runner ================================================ FILE: test/tck/coreprofile/coreprofile/runner/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile.coreprofile project 25.4.0-SNAPSHOT runner jar Piranha - Test - TCK - Core Profile - Core Profile TCK - Runner 3.1 org.jboss.arquillian arquillian-bom ${arquillian.version} pom test cloud.piranha.test.tck.coreprofile.coreprofile installer ${project.version} pom jakarta.tck.coreprofile core-profile-tck-impl ${coreprofile.tck.version} test org.junit.jupiter junit-jupiter test org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.omnifaces.arquillian glassfish-client-ee11 ${glassfish-client-ee11.version} test commons-httpclient commons-httpclient ${commons-httpclient.version} test org.jboss.arquillian.junit5 arquillian-junit5-container ${arquillian.version} test cloud.piranha.arquillian piranha-arquillian-managed ${project.version} test org.apache.maven.plugins maven-compiler-plugin ${java.version} org.apache.maven.plugins maven-failsafe-plugin verify integration-test verify jakarta.tck.coreprofile:core-profile-tck-impl 1 false ================================================ FILE: test/tck/coreprofile/coreprofile/runner/src/test/java/cloud/piranha/tck/core/CustomJsonbSerializationITFix.java ================================================ /* * Copyright (c) 2002-2024 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package cloud.piranha.tck.core; import org.jboss.arquillian.container.spi.event.container.BeforeDeploy; import org.jboss.arquillian.core.api.annotation.Observes; import org.jboss.arquillian.core.spi.LoadableExtension; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.api.asset.EmptyAsset; import org.jboss.shrinkwrap.api.spec.WebArchive; public class CustomJsonbSerializationITFix implements LoadableExtension { @Override public void register(ExtensionBuilder builder) { builder.observer(CustomJsonbSerializationITFix.class); } public void removeService(@Observes BeforeDeploy event) { Archive archive = event.getDeployment().getArchive(); if (archive instanceof WebArchive webArchive) { if (webArchive.getName().equals("CustomJsonbSerializationIT.war")) { webArchive.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml"); } } } } ================================================ FILE: test/tck/coreprofile/coreprofile/runner/src/test/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension ================================================ cloud.piranha.tck.core.CustomJsonbSerializationITFix ================================================ FILE: test/tck/coreprofile/coreprofile/runner/src/test/resources/arquillian.xml ================================================ ================================================ FILE: test/tck/coreprofile/inject/installer/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile.inject project 25.4.0-SNAPSHOT installer pom Piranha - Test - TCK - Core Profile - Inject TCK - Installer ${project.build.directory}/tck org.apache.maven.plugins maven-antrun-plugin pre-integration-test pre-integration-test run ================================================ FILE: test/tck/coreprofile/inject/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile project 25.4.0-SNAPSHOT cloud.piranha.test.tck.coreprofile.inject project pom Piranha - Test - TCK - Core Profile - Inject TCK - Project installer runner ================================================ FILE: test/tck/coreprofile/inject/runner/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile.inject project 25.4.0-SNAPSHOT runner pom Piranha - Test - TCK - Core Profile - Inject TCK - Runner ${project.build.directory}/../../installer/target/tck cloud.piranha.test.tck.coreprofile.inject installer ${project.version} pom org.apache.maven.plugins maven-antrun-plugin integration-test integration-test run ================================================ FILE: test/tck/coreprofile/jsonb/installer/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile.jsonb project 25.4.0-SNAPSHOT installer pom Piranha - Test - TCK - Core Profile - JSON Binding TCK - Installer ${project.build.directory}/tck org.apache.maven.plugins maven-antrun-plugin pre-integration-test pre-integration-test run ================================================ FILE: test/tck/coreprofile/jsonb/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile project 25.4.0-SNAPSHOT cloud.piranha.test.tck.coreprofile.jsonb project pom Piranha - Test - TCK - Core Profile - JSON Binding TCK - Project installer runner ================================================ FILE: test/tck/coreprofile/jsonb/runner/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile.jsonb project 25.4.0-SNAPSHOT runner pom Piranha - Test - TCK - Core Profile - JSON Binding TCK - Runner ${project.build.directory}/../../installer/target/tck cloud.piranha.test.tck.coreprofile.jsonb installer ${project.version} pom org.apache.maven.plugins maven-antrun-plugin integration-test integration-test run ---------------------------------------------------------- Running JSON Binding TCK ${jsonb.tck.version} against Yasson ${yasson.version} ---------------------------------------------------------- ---------------------------------------------------------- Ran JSON Binding TCK ${jsonb.tck.version} against Yasson ${yasson.version} ---------------------------------------------------------- ================================================ FILE: test/tck/coreprofile/jsonp/installer/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile.jsonp project 25.4.0-SNAPSHOT installer pom Piranha - Test - TCK - Core Profile - JSON Processing TCK - Installer ${project.build.directory}/tck org.apache.maven.plugins maven-antrun-plugin pre-integration-test pre-integration-test run ================================================ FILE: test/tck/coreprofile/jsonp/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile project 25.4.0-SNAPSHOT cloud.piranha.test.tck.coreprofile.jsonp project pom Piranha - Test - TCK - Core Profile - JSON Processing TCK - Project installer runner ================================================ FILE: test/tck/coreprofile/jsonp/runner/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile.jsonp project 25.4.0-SNAPSHOT runner pom Piranha - Test - TCK - Core Profile - JSON Processing TCK - Runner ${project.build.directory}/../../installer/target/tck cloud.piranha.test.tck.coreprofile.jsonp installer ${project.version} pom org.apache.maven.plugins maven-antrun-plugin integration-test integration-test run ---------------------------------------------------------- Running JSON Processing TCK ${jsonp.tck.version} against Parsson ${parsson.version} ---------------------------------------------------------- ---------------------------------------------------------- Ran JSON Processing TCK ${jsonp.tck.version} against Parsson ${parsson.version} ---------------------------------------------------------- ================================================ FILE: test/tck/coreprofile/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck project 25.4.0-SNAPSHOT cloud.piranha.test.tck.coreprofile project pom Piranha - Test - TCK - Core Profile 3.0.0 4.1.0 11.0.0 2.0.2 3.0.0 2.1.1 4.0.1 2.6 cloud.piranha bom ${project.version} pom import jakarta.tck sigtest-maven-plugin ${sigtest-maven-plugin.version} annotations cdi coreprofile inject jsonb jsonp rest ================================================ FILE: test/tck/coreprofile/rest/installer/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile.rest project 25.4.0-SNAPSHOT installer pom Piranha - Test - TCK - Core Profile - REST TCK - Installer ${project.build.directory}/tck org.apache.maven.plugins maven-antrun-plugin pre-integration-test pre-integration-test run ================================================ FILE: test/tck/coreprofile/rest/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile project 25.4.0-SNAPSHOT cloud.piranha.test.tck.coreprofile.rest project pom Piranha - Test - TCK - Core Profile - REST TCK - Project installer runner ================================================ FILE: test/tck/coreprofile/rest/runner/pom.xml ================================================ 4.0.0 cloud.piranha.test.tck.coreprofile.rest project 25.4.0-SNAPSHOT runner jar Piranha - Test - TCK - Core Profile - REST TCK - Runner 3.1 3.0 ${project.build.directory}/jimage org.jboss.arquillian arquillian-bom ${arquillian.version} pom test cloud.piranha.arquillian piranha-arquillian-managed ${project.version} test jakarta.ws.rs jakarta-restful-ws-tck ${rest.tck.version} test org.jboss.arquillian.junit5 arquillian-junit5-container ${arquillian.version} test org.junit.jupiter junit-jupiter test org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.hamcrest hamcrest ${hamcrest.version} test org.omnifaces.arquillian glassfish-client-ee11 ${glassfish-client-ee11.version} test commons-httpclient commons-httpclient ${commons-httpclient.version} test jakarta.tck sigtest-maven-plugin test org.jacoco jacoco-maven-plugin true org.apache.maven.plugins maven-failsafe-plugin verify integration-test verify jakarta.ws.rs:jakarta-restful-ws-tck 1 false 120 piranha true true ee.jakarta.tck.ws.rs.lib.implementation.sun.common.SunRIURL org.glassfish.jersey.servlet.ServletContainer ${jimage.dir} ${settings.localRepository}/jakarta/ws/rs/jakarta.ws.rs-api/4.0.0/jakarta.ws.rs-api-4.0.0.jar:${jimage.dir}/java.base:${jimage.dir}/java.rmi:${jimage.dir}/java.sql:${jimage.dir}/java.naming localhost 8080 j2ee j2ee ================================================ FILE: test/tck/coreprofile/rest/runner/src/test/resources/arquillian.xml ================================================ 8080 j2ee j2ee DIRECTOR ================================================ FILE: test/tck/pom.xml ================================================ 4.0.0 cloud.piranha.test project 25.4.0-SNAPSHOT cloud.piranha.test.tck project pom Piranha - Test - TCK coreprofile ================================================ FILE: test/webprofile/helloworld/pom.xml ================================================ 4.0.0 cloud.piranha.test.webprofile project 25.4.0-SNAPSHOT piranha-test-webprofile-helloworld war Piranha - Test - Web Profile - Hello World webprofile UTF-8 jakarta.platform jakarta.jakartaee-web-api provided cloud.piranha.dist piranha-dist-webprofile ${project.version} provided jar true org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test piranha-test-webprofile-helloworld cloud.piranha.maven piranha-maven-plugin ${project.version} pre-integration-test pre-integration-test start webprofile 9001 post-integration-test post-integration-test stop org.apache.maven.plugins maven-war-plugin false ================================================ FILE: test/webprofile/helloworld/src/main/java/helloworld/ApplicationBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package helloworld; import jakarta.inject.Named; import jakarta.enterprise.context.ApplicationScoped; /** * The one and only application bean. * * @author Manfred Riem (mriem@manorrock.com) */ @Named(value = "applicationBean") @ApplicationScoped public class ApplicationBean { } ================================================ FILE: test/webprofile/helloworld/src/main/webapp/WEB-INF/beans.xml ================================================ ================================================ FILE: test/webprofile/helloworld/src/main/webapp/helloworld.jsp ================================================ <%@page contentType="text/html" pageEncoding="UTF-8"%> Hello World!

Hello World!

================================================ FILE: test/webprofile/helloworld/src/main/webapp/index.html ================================================ Hello World!
Hello World!
================================================ FILE: test/webprofile/helloworld/src/test/java/helloworld/HelloWorldIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package helloworld; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** * The 'Hello World!' integration test. * * @author Manfred Riem (mriem@manorrock.com) */ class HelloWorldIT { /** * Test index.html. * * @throws Exception when a serious error occurs. */ @Test void testIndexHtml() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:9001/piranha-test-webprofile-helloworld")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello World!")); } /** * Test helloworld.jsp. * * @throws Exception when a serious error occurs. */ @Test void testHelloWorldJsp() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost:9001/piranha-test-webprofile-helloworld/helloworld.jsp")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Hello World!")); } } ================================================ FILE: test/webprofile/integration/pom.xml ================================================ 4.0.0 cloud.piranha.test.webprofile project 25.4.0-SNAPSHOT piranha-test-webprofile-integration war Piranha - Test - Web Profile - Distribution Integration Tests UTF-8 jakarta.platform jakarta.jakartaee-web-api provided cloud.piranha.arquillian piranha-arquillian-managed ${project.version} test org.jboss.arquillian.junit5 arquillian-junit5-container ${arquillian.version} test org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test piranha-test-webprofile-integration org.apache.maven.plugins maven-failsafe-plugin integration-test verify 10 webprofile org.apache.maven.plugins maven-war-plugin false ================================================ FILE: test/webprofile/integration/src/main/java/integration/DependencyInjectionBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package integration; import jakarta.enterprise.context.ApplicationScoped; /** * The one and only Dependency Injection bean. * * @author Manfred Riem (mriem@manorrock.com) */ @ApplicationScoped public class DependencyInjectionBean { /** * Get the string to validate dependency injection works. * * @return the string. */ public String dependencyInjection() { return "Dependency Injection works!"; } } ================================================ FILE: test/webprofile/integration/src/main/java/integration/ExpressionBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package integration; import jakarta.inject.Named; import jakarta.enterprise.context.ApplicationScoped; /** * The Expression bean. * * @author Manfred Riem (mriem@manorrock.com) */ @Named(value = "expressionBean") @ApplicationScoped public class ExpressionBean { /** * Get the message. * * @return the message. */ public String getMessage() { return "Expression Language works!"; } } ================================================ FILE: test/webprofile/integration/src/main/java/integration/FacesBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package integration; import jakarta.inject.Named; import jakarta.enterprise.context.ApplicationScoped; /** * The Faces bean. * * @author Manfred Riem (mriem@manorrock.com) */ @Named(value = "facesBean") @ApplicationScoped public class FacesBean { /** * Get the message. * * @return the message. */ public String getMessage() { return "Faces works!"; } } ================================================ FILE: test/webprofile/integration/src/main/java/integration/IntegrationApplication.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package integration; import jakarta.ws.rs.ApplicationPath; import jakarta.ws.rs.core.Application; /** * The HelloWorld application. * * @author Manfred Riem (mriem@manorrock.com) */ @ApplicationPath("rest") public class IntegrationApplication extends Application { } ================================================ FILE: test/webprofile/integration/src/main/java/integration/IntegrationBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package integration; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Inject; import jakarta.json.Json; import jakarta.json.stream.JsonParser; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import java.io.StringReader; /** * The Integration bean. * * @author Manfred Riem (mriem@manorrock.com) */ @Path("") @RequestScoped public class IntegrationBean { /** * Stores the DependencyInjectionBean. */ @Inject private DependencyInjectionBean dependencyInjection; /** * Stores the intercept bean. */ @Inject private InterceptBean interceptBean; /** * Validate the correct string is returned using the bean injected using the * Inject annotation. * * @return 'Dependency Injection works!'. */ @GET @Path("/dependencyInjection") public String dependencyInjection() { return dependencyInjection.dependencyInjection(); } /** * Validate the correct string is returned using an interceptor. * * @return 'Interceptor work!'. */ @GET @Path("/intercept") public String intercept() { return interceptBean.intercept(); } /** * Validate JSON Binding works. * * @return 'JSON Binding works!' in JSON format. */ @GET @Produces("application/json") @Path("/jsonb") public Jsonb jsonb() { return new Jsonb(); } /** * Validate JSON Processing works. * * @param jsonString a JSON string. * @return 'JSON processing works!' in JSON format. */ @POST @Produces("application/json") @Consumes("application/json") @Path("/jsonp") public Jsonb jsonp(String jsonString) { Jsonb jsonb = new Jsonb(); try ( JsonParser parser = Json.createParser(new StringReader(jsonString));) { parser.next(); String string = parser.getString(); jsonb.setString(string); } return jsonb; } /** * Say 'REST works!'. * * @return 'Hello World!'. */ @GET @Path("/rest") public String rest() { return "REST works!"; } } ================================================ FILE: test/webprofile/integration/src/main/java/integration/IntegrationServlet.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package integration; import java.io.IOException; import java.io.PrintWriter; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * The Servlet testing Jakarta Servlet integration. * * @author Manfred Riem (mriem@manorrock.com) */ @WebServlet(name = "IntegrationServlet", urlPatterns = {"/integrationServlet"}) public class IntegrationServlet extends HttpServlet { /** * Handles the HTTP GET method. * * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); try (PrintWriter out = response.getWriter()) { out.println(""); out.println(""); out.println(""); out.println("Servlet works!"); out.println(""); out.println(""); out.println("

Servlet works!

"); out.println(""); out.println(""); } } } ================================================ FILE: test/webprofile/integration/src/main/java/integration/InterceptBean.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package integration; import jakarta.enterprise.context.ApplicationScoped; import jakarta.interceptor.Interceptors; /** * The Intercept bean. * * @author Manfred Riem (mriem@manorrock.com) */ @ApplicationScoped @Interceptors(InterceptInterceptor.class) public class InterceptBean { /** * Get the string to validate the interceptor works. * * @return the string. */ public String intercept() { return "Inceptor does NOT work!"; } } ================================================ FILE: test/webprofile/integration/src/main/java/integration/InterceptInterceptor.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package integration; import jakarta.interceptor.AroundInvoke; import jakarta.interceptor.Interceptor; import jakarta.interceptor.InvocationContext; /** * The intercept interceptor. * * @author Manfred Riem (mriem@manorrock.com) */ @Interceptor public class InterceptInterceptor { /** * Intercept the call and return 'Interceptor works!'. * * @param context the invocation context. * @return 'Interceptor works!'. */ @AroundInvoke public Object intercepted(InvocationContext context) { return "Interceptor works!"; } } ================================================ FILE: test/webprofile/integration/src/main/java/integration/Jsonb.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package integration; /** * The JSON Binding POJO. * * @author Manfred Riem (mriem@manorrock.com) */ public class Jsonb { /** * Stores the string. */ private String string = "JSON Binding works!"; /** * Get the string. * * @return the string. */ public String getString() { return string; } /** * Set the string. * * @param string the string. */ public void setString(String string) { this.string = string; } } ================================================ FILE: test/webprofile/integration/src/main/webapp/WEB-INF/beans.xml ================================================ ================================================ FILE: test/webprofile/integration/src/main/webapp/WEB-INF/web.xml ================================================ Faces Servlet jakarta.faces.webapp.FacesServlet 1 Faces Servlet *.xhtml 30 index.xhtml ================================================ FILE: test/webprofile/integration/src/main/webapp/expression.jsp ================================================ <%@page contentType="text/html" pageEncoding="UTF-8"%> ${expressionBean.message}

${expressionBean.message}

================================================ FILE: test/webprofile/integration/src/main/webapp/faces.xhtml ================================================ #{facesBean.message}

#{facesBean.message}

================================================ FILE: test/webprofile/integration/src/main/webapp/jstl.jsp ================================================ <%@page contentType="text/html" pageEncoding="UTF-8"%> <%@taglib uri="jakarta.tags.core" prefix="c"%> <c:out value="${'JSTL works!'}"/>

================================================ FILE: test/webprofile/integration/src/main/webapp/pages.jsp ================================================ <%@page contentType="text/html" pageEncoding="UTF-8"%> Pages works!

Pages works!

================================================ FILE: test/webprofile/integration/src/test/java/integration/IntegrationIT.java ================================================ /* * Copyright (c) 2002-2025 Manorrock.com. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package integration; import java.io.File; import java.net.URI; import java.net.URL; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.RunAsClient; import org.jboss.arquillian.junit5.ArquillianExtension; import org.jboss.arquillian.test.api.ArquillianResource; import static org.jboss.shrinkwrap.api.ShrinkWrap.create; import org.jboss.shrinkwrap.api.spec.WebArchive; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; /** * The Piranha Web Profile distribution integration tests. * *
    *
  1. testDependencyInjection validates Jakarta Dependency Injection works
  2. *
  3. testExpression validates Jakarta Expression Language works
  4. *
  5. testFaces validates Jakarta Faces works
  6. *
  7. testInterceptor validates Jakarta Interceptors works
  8. *
  9. testJsonBinding validates Jakarta JSON binding works
  10. *
  11. testJsonProcessing validates Jakarta JSON processing works
  12. *
  13. testJstl validates Jakarta Standard Tag Library works
  14. *
  15. testPages validates Jakarta Pages works
  16. *
  17. testServlet validates Jakarta Servlet works
  18. *
  19. testREST validates Jakarta REST works
  20. *
* * @author Manfred Riem (mriem@manorrock.com) */ @ExtendWith(ArquillianExtension.class) class IntegrationIT { @ArquillianResource private URL baseUrl; @Deployment(testable = false) public static WebArchive createDeployment() { return create(WebArchive.class) .addClass(DependencyInjectionBean.class) .addClass(ExpressionBean.class) .addClass(FacesBean.class) .addClass(IntegrationApplication.class) .addClass(IntegrationBean.class) .addClass(IntegrationServlet.class) .addClass(InterceptBean.class) .addClass(InterceptInterceptor.class) .addClass(Jsonb.class) .addAsWebInfResource(new File("src/main/webapp/WEB-INF/beans.xml")) .addAsWebInfResource(new File("src/main/webapp/WEB-INF/web.xml")) .addAsWebResource(new File("src/main/webapp/expression.jsp")) .addAsWebResource(new File("src/main/webapp/faces.xhtml")) .addAsWebResource(new File("src/main/webapp/pages.jsp")); } /** * Test dependency injection. * * @throws Exception when a serious error occurs. */ @Test @RunAsClient void testDependencyInjection() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI(baseUrl + "/rest/dependencyInjection")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Dependency Injection works!")); } /** * Test Expression Language. * * @throws Exception when a serious error occurs. */ @Test @RunAsClient void testExpressionLanguage() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI(baseUrl + "/expression.jsp")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Expression Language works!")); } /** * Test Faces. * * @throws Exception when a serious error occurs. */ @Test @RunAsClient void testFaces() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI(baseUrl + "faces.xhtml")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Faces works!")); } /** * Test interceptors. * * @throws Exception when a serious error occurs. */ @Test @RunAsClient void testInterceptor() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI(baseUrl + "/rest/intercept")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Interceptor works!")); } /** * Test JSON binding. * * @throws Exception when a serious error occurs. */ @Test @RunAsClient void testJsonBinding() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI(baseUrl + "/rest/jsonb")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("{\"string\":\"JSON Binding works!\"}")); } /** * Test JSON Processing. * * @throws Exception when a serious error occurs. */ @Test @RunAsClient void testJsonProcessing() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI(baseUrl + "/rest/jsonp")) .POST(HttpRequest.BodyPublishers.ofString("\"JSON Processing works!\"")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("{\"string\":\"JSON Processing works!\"}")); } /** * Test JSTL. * * @throws Exception when a serious error occurs. */ @Disabled @Test void testJstl() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI(baseUrl + "/jstl.jsp")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); System.out.println(response.body()); assertTrue(response.body().contains("JSTL works!")); assertFalse(response.body().contains("c:out")); } /** * Test Pages. * * @throws Exception when a serious error occurs. */ @Test @RunAsClient void testPages() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI(baseUrl + "/pages.jsp")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Pages works!")); } /** * Test Servlet. * * @throws Exception when a serious error occurs. */ @Test @RunAsClient void testServlet() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI(baseUrl + "/integrationServlet")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("Servlet works!")); } /** * Test REST. * * @throws Exception when a serious error occurs. */ @Test @RunAsClient void testREST() throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI(baseUrl + "/rest/rest")) .build(); HttpResponse response = client.send(request, BodyHandlers.ofString()); assertTrue(response.body().contains("REST works!")); } } ================================================ FILE: test/webprofile/pom.xml ================================================ 4.0.0 cloud.piranha.test project 25.4.0-SNAPSHOT cloud.piranha.test.webprofile project pom Piranha - Test - Web Profile - Project cloud.piranha.dist piranha-dist-webprofile ${project.version} provided pom helloworld integration