From e9a1f99e272e546362e378562f049b01bce53035 Mon Sep 17 00:00:00 2001
From: Manfred Riem
Date: Sun, 8 Dec 2024 11:49:52 -0600
Subject: [PATCH] Fixes #4325 - Add HandlesTypes extension
---
.../piranha/core/api/HandlesTypesManager.java | 103 +++++++++
.../core/api/WebApplicationManager.java | 14 ++
.../core/impl/DefaultWebApplication.java | 56 +++--
.../impl/DefaultWebApplicationManager.java | 45 ++--
extension/coreprofile/pom.xml | 6 +
.../coreprofile/CoreProfileExtension.java | 2 +
.../src/main/java/module-info.java | 2 +
extension/handlestypes/pom.xml | 74 +++++++
.../handlestypes/HandlesTypesExtension.java | 53 +++++
.../InternalHandlesTypesInitializer.java | 206 ++++++++++++++++++
.../internal/InternalHandlesTypesManager.java | 124 +++++++++++
.../src/main/java/module-info.java | 40 ++++
.../HandlesTypesExtensionTest.java | 52 +++++
.../InternalHandlesTypesInitializerTest.java | 96 ++++++++
.../InternalHandlesTypesManagerTest.java | 48 ++++
.../handlestypes/internal/TestA.java | 37 ++++
.../handlestypes/internal/TestB.java | 41 ++++
.../handlestypes/internal/TestC.java | 36 +++
.../handlestypes/internal/TestD.java | 36 +++
.../TestServletContainerInitializer.java | 47 ++++
extension/pom.xml | 1 +
.../internal/InternalWebXmlProcessor.java | 4 -
.../piranha/feature/webapp/WebAppFeature.java | 2 +-
.../impl/AliasedDirectoryResource.java | 17 +-
.../resource/impl/DefaultResourceManager.java | 2 +-
test/coreprofile/arquillian/pom.xml | 7 +
test/coreprofile/no_servlet_class/pom.xml | 142 ++++++++++++
.../no_servlet_class/EchoApplication.java | 42 ++++
.../no_servlet_class/EchoBean.java | 49 +++++
.../src/main/webapp/WEB-INF/web.xml | 12 +
.../coreprofile/no_servlet_class/EchoIT.java | 24 ++
test/coreprofile/pom.xml | 1 +
32 files changed, 1382 insertions(+), 39 deletions(-)
create mode 100644 core/api/src/main/java/cloud/piranha/core/api/HandlesTypesManager.java
create mode 100644 extension/handlestypes/pom.xml
create mode 100644 extension/handlestypes/src/main/java/cloud/piranha/extension/handlestypes/HandlesTypesExtension.java
create mode 100644 extension/handlestypes/src/main/java/cloud/piranha/extension/handlestypes/internal/InternalHandlesTypesInitializer.java
create mode 100644 extension/handlestypes/src/main/java/cloud/piranha/extension/handlestypes/internal/InternalHandlesTypesManager.java
create mode 100644 extension/handlestypes/src/main/java/module-info.java
create mode 100644 extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/HandlesTypesExtensionTest.java
create mode 100644 extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/InternalHandlesTypesInitializerTest.java
create mode 100644 extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/InternalHandlesTypesManagerTest.java
create mode 100644 extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestA.java
create mode 100644 extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestB.java
create mode 100644 extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestC.java
create mode 100644 extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestD.java
create mode 100644 extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestServletContainerInitializer.java
create mode 100644 test/coreprofile/no_servlet_class/pom.xml
create mode 100644 test/coreprofile/no_servlet_class/src/main/java/cloud/piranha/test/coreprofile/no_servlet_class/EchoApplication.java
create mode 100644 test/coreprofile/no_servlet_class/src/main/java/cloud/piranha/test/coreprofile/no_servlet_class/EchoBean.java
create mode 100644 test/coreprofile/no_servlet_class/src/main/webapp/WEB-INF/web.xml
create mode 100644 test/coreprofile/no_servlet_class/src/test/java/cloud/piranha/test/coreprofile/no_servlet_class/EchoIT.java
diff --git a/core/api/src/main/java/cloud/piranha/core/api/HandlesTypesManager.java b/core/api/src/main/java/cloud/piranha/core/api/HandlesTypesManager.java
new file mode 100644
index 0000000000..c9881c7df4
--- /dev/null
+++ b/core/api/src/main/java/cloud/piranha/core/api/HandlesTypesManager.java
@@ -0,0 +1,103 @@
+/*
+ * 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.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);
+}
diff --git a/core/api/src/main/java/cloud/piranha/core/api/WebApplicationManager.java b/core/api/src/main/java/cloud/piranha/core/api/WebApplicationManager.java
index 2e7045c5da..72eb4895a2 100644
--- a/core/api/src/main/java/cloud/piranha/core/api/WebApplicationManager.java
+++ b/core/api/src/main/java/cloud/piranha/core/api/WebApplicationManager.java
@@ -69,6 +69,13 @@ public interface WebApplicationManager {
*/
DispatcherManager getDispatcherManager();
+ /**
+ * Get the HandlesTypes manager.
+ *
+ * @return the HandlesTypes manager.
+ */
+ HandlesTypesManager getHandlesTypesManager();
+
/**
* Get the HTTP session manager.
*
@@ -153,6 +160,13 @@ public interface WebApplicationManager {
*/
void setErrorPageManager(ErrorPageManager errorPageManager);
+ /**
+ * Set the HandlesTypes manager.
+ *
+ * @param handlesTypesManager the HandlesTypes manager.
+ */
+ void setHandlesTypesManager(HandlesTypesManager handlesTypesManager);
+
/**
* Set the HTTP session manager.
*
diff --git a/core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplication.java b/core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplication.java
index 084051203e..4686b4ca49 100644
--- a/core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplication.java
+++ b/core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplication.java
@@ -93,6 +93,7 @@
import java.util.stream.Stream;
import cloud.piranha.core.api.WebApplicationManager;
import static java.lang.System.Logger.Level.INFO;
+import java.util.Arrays;
/**
* The default WebApplication.
@@ -505,14 +506,18 @@ public Dynamic addServlet(String servletName, String className) {
DefaultServletEnvironment servletEnvironment = servletEnvironments.get(servletName);
if (servletEnvironment == null) {
servletEnvironment = new DefaultServletEnvironment(this, servletName);
- servletEnvironment.setClassName(className);
+ 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;
}
- servletEnvironment.setClassName(className);
+ if (className != null && !className.isBlank()) {
+ servletEnvironment.setClassName(className);
+ }
}
return servletEnvironment;
}
@@ -984,7 +989,7 @@ public void initializeFilters() {
try {
environment.initialize();
environment.getFilter().init(environment);
- } catch (Exception e) {
+ } catch (Throwable e) {
LOGGER.log(WARNING, () -> "Unable to initialize filter: " + environment.getFilterName(), e);
environment.setStatus(UNAVAILABLE);
}
@@ -1010,20 +1015,41 @@ public void initializeInitializers() {
try {
HandlesTypes annotation = initializer.getClass().getAnnotation(HandlesTypes.class);
Set> classes = Collections.emptySet();
- if (annotation != null && manager.getAnnotationManager() != null) {
+ if (annotation != null) {
Class>[] value = annotation.value();
- // Get instances
- Stream> instances = manager.getAnnotationManager()
- .getInstances(value).stream();
- // Get classes by target type
- List> annotations = manager.getAnnotationManager()
- .getAnnotations(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);
+ Stream> classStream = annotations.stream().map(AnnotationInfo::getTargetType);
- classes = Stream.concat(instances, classStream).collect(Collectors.toSet());
- classes.addAll(manager.getAnnotationManager().getAnnotatedClasses(annotation.value()));
+ 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;
@@ -1031,7 +1057,7 @@ public void initializeInitializers() {
} finally {
source = null;
}
- } catch (Exception e) {
+ } catch (Throwable e) {
LOGGER.log(WARNING, () -> "Initializer " + initializer.getClass().getName() + " failing onStartup", e);
error = true;
}
@@ -1084,7 +1110,7 @@ private void initializeServlet(DefaultServletEnvironment environment) {
}
environment.getServlet().init(environment);
LOGGER.log(DEBUG, "Initialized servlet: {0}", environment.servletName);
- } catch (Exception e) {
+ } catch (Throwable e) {
LOGGER.log(WARNING, () -> "Unable to initialize servlet: " + environment.className, e);
environment.setStatus(ServletEnvironment.UNAVAILABLE);
diff --git a/core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplicationManager.java b/core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplicationManager.java
index a1184e352f..368fa2a7bf 100644
--- a/core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplicationManager.java
+++ b/core/impl/src/main/java/cloud/piranha/core/impl/DefaultWebApplicationManager.java
@@ -31,6 +31,7 @@
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;
@@ -49,27 +50,32 @@
* @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.
*/
@@ -79,22 +85,22 @@ public class DefaultWebApplicationManager implements WebApplicationManager {
* 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.
*/
@@ -104,7 +110,7 @@ public class DefaultWebApplicationManager implements WebApplicationManager {
* Stores the security manager.
*/
protected SecurityManager securityManager = new DefaultSecurityManager();
-
+
/**
* Stores the servlet request manager.
*/
@@ -125,11 +131,6 @@ public AsyncManager getAsyncManager() {
return asyncManager;
}
- /**
- * Get the dispatcher manager.
- *
- * @return the dispatcher manager.
- */
@Override
public DispatcherManager getDispatcherManager() {
return dispatcherManager;
@@ -140,6 +141,11 @@ public ErrorPageManager getErrorPageManager() {
return errorPageManager;
}
+ @Override
+ public HandlesTypesManager getHandlesTypesManager() {
+ return handlesTypesManager;
+ }
+
@Override
public HttpSessionManager getHttpSessionManager() {
return httpSessionManager;
@@ -201,8 +207,8 @@ public void setErrorPageManager(ErrorPageManager errorPageManager) {
}
@Override
- public void setServletRequestManager(ServletRequestManager servletRequestManager) {
- this.servletRequestManager = servletRequestManager;
+ public void setHandlesTypesManager(HandlesTypesManager handlesTypesManager) {
+ this.handlesTypesManager = handlesTypesManager;
}
@Override
@@ -240,6 +246,11 @@ 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;
diff --git a/extension/coreprofile/pom.xml b/extension/coreprofile/pom.xml
index 9c6b42c82b..6cc854f67e 100644
--- a/extension/coreprofile/pom.xml
+++ b/extension/coreprofile/pom.xml
@@ -37,6 +37,12 @@
${project.version}
compile
+
+ cloud.piranha.extension
+ piranha-extension-handlestypes
+ ${project.version}
+ compile
+
cloud.piranha.extension
piranha-extension-herring
diff --git a/extension/coreprofile/src/main/java/cloud/piranha/extension/coreprofile/CoreProfileExtension.java b/extension/coreprofile/src/main/java/cloud/piranha/extension/coreprofile/CoreProfileExtension.java
index 53ab1598f5..2942966c5f 100644
--- a/extension/coreprofile/src/main/java/cloud/piranha/extension/coreprofile/CoreProfileExtension.java
+++ b/extension/coreprofile/src/main/java/cloud/piranha/extension/coreprofile/CoreProfileExtension.java
@@ -31,6 +31,7 @@
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.scinitializer.ServletContainerInitializerExtension;
import cloud.piranha.extension.webxml.WebXmlExtension;
@@ -45,6 +46,7 @@ public class CoreProfileExtension implements WebApplicationExtension {
@Override
public void extend(WebApplicationExtensionContext context) {
+ context.add(HandlesTypesExtension.class); // HandlesTypes support
context.add(HerringExtension.class); // Herring (JNDI)
context.add(WebXmlExtension.class);
context.add(getAnnotationScanExtensionClass());
diff --git a/extension/coreprofile/src/main/java/module-info.java b/extension/coreprofile/src/main/java/module-info.java
index 76a2008e78..a7e2cff608 100644
--- a/extension/coreprofile/src/main/java/module-info.java
+++ b/extension/coreprofile/src/main/java/module-info.java
@@ -34,6 +34,7 @@
*
*
* - Annotation Scanning
+ * - HandlesTypes support
* - Herring (JNDI)
* - Jersey (REST)
* - Parsson (JSON)
@@ -50,6 +51,7 @@
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.scinitializer;
diff --git a/extension/handlestypes/pom.xml b/extension/handlestypes/pom.xml
new file mode 100644
index 0000000000..e965f0293d
--- /dev/null
+++ b/extension/handlestypes/pom.xml
@@ -0,0 +1,74 @@
+
+
+
+ 4.0.0
+
+ cloud.piranha.extension
+ project
+ 24.12.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
+ 5.10.3
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ test
+ 5.10.3
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+ 5.10.3
+
+
+
+
+
+ maven-compiler-plugin
+
+
+
+ testCompile
+
+ test-compile
+
+
+ false
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ false
+
+
+
+
+
diff --git a/extension/handlestypes/src/main/java/cloud/piranha/extension/handlestypes/HandlesTypesExtension.java b/extension/handlestypes/src/main/java/cloud/piranha/extension/handlestypes/HandlesTypesExtension.java
new file mode 100644
index 0000000000..14f4f25c8d
--- /dev/null
+++ b/extension/handlestypes/src/main/java/cloud/piranha/extension/handlestypes/HandlesTypesExtension.java
@@ -0,0 +1,53 @@
+/*
+ * 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.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 {
+
+ /**
+ * Configure the web application.
+ *
+ * @param webApplication the web application.
+ */
+ @Override
+ public void configure(WebApplication webApplication) {
+ webApplication.getManager().setHandlesTypesManager(
+ new InternalHandlesTypesManager());
+ webApplication.addInitializer(new InternalHandlesTypesInitializer());
+ }
+}
diff --git a/extension/handlestypes/src/main/java/cloud/piranha/extension/handlestypes/internal/InternalHandlesTypesInitializer.java b/extension/handlestypes/src/main/java/cloud/piranha/extension/handlestypes/internal/InternalHandlesTypesInitializer.java
new file mode 100644
index 0000000000..fe46cb2b12
--- /dev/null
+++ b/extension/handlestypes/src/main/java/cloud/piranha/extension/handlestypes/internal/InternalHandlesTypesInitializer.java
@@ -0,0 +1,206 @@
+/*
+ * 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.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;
+ }
+}
diff --git a/extension/handlestypes/src/main/java/cloud/piranha/extension/handlestypes/internal/InternalHandlesTypesManager.java b/extension/handlestypes/src/main/java/cloud/piranha/extension/handlestypes/internal/InternalHandlesTypesManager.java
new file mode 100644
index 0000000000..ce6d62954f
--- /dev/null
+++ b/extension/handlestypes/src/main/java/cloud/piranha/extension/handlestypes/internal/InternalHandlesTypesManager.java
@@ -0,0 +1,124 @@
+/*
+ * 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.extension.handlestypes.internal;
+
+import cloud.piranha.core.api.HandlesTypesManager;
+import java.util.HashMap;
+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) {
+ 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) {
+ 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) {
+ 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;
+ }
+}
diff --git a/extension/handlestypes/src/main/java/module-info.java b/extension/handlestypes/src/main/java/module-info.java
new file mode 100644
index 0000000000..e8f3be2498
--- /dev/null
+++ b/extension/handlestypes/src/main/java/module-info.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+/**
+ * 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;
+}
diff --git a/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/HandlesTypesExtensionTest.java b/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/HandlesTypesExtensionTest.java
new file mode 100644
index 0000000000..d671f76107
--- /dev/null
+++ b/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/HandlesTypesExtensionTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.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());
+ }
+}
diff --git a/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/InternalHandlesTypesInitializerTest.java b/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/InternalHandlesTypesInitializerTest.java
new file mode 100644
index 0000000000..c847dfcea5
--- /dev/null
+++ b/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/InternalHandlesTypesInitializerTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.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());
+ }
+}
diff --git a/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/InternalHandlesTypesManagerTest.java b/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/InternalHandlesTypesManagerTest.java
new file mode 100644
index 0000000000..5057f01990
--- /dev/null
+++ b/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/InternalHandlesTypesManagerTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.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));
+ }
+}
diff --git a/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestA.java b/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestA.java
new file mode 100644
index 0000000000..d23d6ef9a4
--- /dev/null
+++ b/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestA.java
@@ -0,0 +1,37 @@
+/*
+ * 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 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 {
+}
diff --git a/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestB.java b/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestB.java
new file mode 100644
index 0000000000..003e472624
--- /dev/null
+++ b/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestB.java
@@ -0,0 +1,41 @@
+/*
+ * 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.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 {
+
+}
diff --git a/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestC.java b/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestC.java
new file mode 100644
index 0000000000..a639b7bc40
--- /dev/null
+++ b/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestC.java
@@ -0,0 +1,36 @@
+/*
+ * 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 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 {
+}
diff --git a/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestD.java b/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestD.java
new file mode 100644
index 0000000000..f818184274
--- /dev/null
+++ b/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestD.java
@@ -0,0 +1,36 @@
+/*
+ * 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 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 {
+}
diff --git a/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestServletContainerInitializer.java b/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestServletContainerInitializer.java
new file mode 100644
index 0000000000..4f4fd8e8c8
--- /dev/null
+++ b/extension/handlestypes/src/test/java/cloud/piranha/extension/handlestypes/internal/TestServletContainerInitializer.java
@@ -0,0 +1,47 @@
+/*
+ * 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.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 {
+ }
+}
diff --git a/extension/pom.xml b/extension/pom.xml
index c513828f18..45a9bac255 100644
--- a/extension/pom.xml
+++ b/extension/pom.xml
@@ -30,6 +30,7 @@
exousia
expressly
fileupload
+ handlestypes
hazelcast
herring
hibernate-validator
diff --git a/extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/InternalWebXmlProcessor.java b/extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/InternalWebXmlProcessor.java
index cb377b18a2..760aaa11e7 100644
--- a/extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/InternalWebXmlProcessor.java
+++ b/extension/webxml/src/main/java/cloud/piranha/extension/webxml/internal/InternalWebXmlProcessor.java
@@ -514,10 +514,6 @@ private void processServlets(WebApplication webApplication, WebXml webXml) {
if (!isEmpty(jspFile)) {
dynamic = webApplication.addJspFile(servlet.getServletName(), jspFile);
} else {
- if (isEmpty(servlet.getClassName())) {
- servlet.setClassName(servlet.getServletName());
- LOGGER.log(DEBUG, "Setting servlet-class from servlet-name");
- }
dynamic = webApplication.addServlet(servlet.getServletName(), servlet.getClassName());
}
diff --git a/feature/webapp/src/main/java/cloud/piranha/feature/webapp/WebAppFeature.java b/feature/webapp/src/main/java/cloud/piranha/feature/webapp/WebAppFeature.java
index 929d1090c3..71cffd02b5 100644
--- a/feature/webapp/src/main/java/cloud/piranha/feature/webapp/WebAppFeature.java
+++ b/feature/webapp/src/main/java/cloud/piranha/feature/webapp/WebAppFeature.java
@@ -200,7 +200,7 @@ public void init() {
try {
webApplication.initialize();
- } catch (Exception e) {
+ } catch (Throwable e) {
LOGGER.log(ERROR, "Failed to initialize web application");
}
}
diff --git a/resource/impl/src/main/java/cloud/piranha/resource/impl/AliasedDirectoryResource.java b/resource/impl/src/main/java/cloud/piranha/resource/impl/AliasedDirectoryResource.java
index 7a6ea7aad5..752b150b3e 100644
--- a/resource/impl/src/main/java/cloud/piranha/resource/impl/AliasedDirectoryResource.java
+++ b/resource/impl/src/main/java/cloud/piranha/resource/impl/AliasedDirectoryResource.java
@@ -31,10 +31,15 @@
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;
/**
@@ -112,7 +117,17 @@ public InputStream getResourceAsStream(String location) {
@Override
public Stream getAllLocations() {
- return Stream.empty();
+ 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();
+ }
}
/**
diff --git a/resource/impl/src/main/java/cloud/piranha/resource/impl/DefaultResourceManager.java b/resource/impl/src/main/java/cloud/piranha/resource/impl/DefaultResourceManager.java
index bd985e3990..62c2667ede 100644
--- a/resource/impl/src/main/java/cloud/piranha/resource/impl/DefaultResourceManager.java
+++ b/resource/impl/src/main/java/cloud/piranha/resource/impl/DefaultResourceManager.java
@@ -143,7 +143,7 @@ public InputStream getResourceAsStream(String location) {
@Override
public Stream getAllLocations() {
- return resources.stream().flatMap(Resource::getAllLocations);
+ return resources.parallelStream().flatMap(Resource::getAllLocations);
}
@Override
diff --git a/test/coreprofile/arquillian/pom.xml b/test/coreprofile/arquillian/pom.xml
index 7e600f6f66..df7c523984 100644
--- a/test/coreprofile/arquillian/pom.xml
+++ b/test/coreprofile/arquillian/pom.xml
@@ -76,6 +76,13 @@
+
org.apache.maven.plugins
diff --git a/test/coreprofile/no_servlet_class/pom.xml b/test/coreprofile/no_servlet_class/pom.xml
new file mode 100644
index 0000000000..304ddea919
--- /dev/null
+++ b/test/coreprofile/no_servlet_class/pom.xml
@@ -0,0 +1,142 @@
+
+
+
+ 4.0.0
+
+ cloud.piranha.test.coreprofile
+ project
+ 24.12.0-SNAPSHOT
+
+ piranha-test-coreprofile-no_servlet_class
+ war
+ Piranha - Test - Core Profile - No servlet-class specified
+
+
+ ${project.version}
+ 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
+ ${piranha.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
+
+
+
+
+
+
+
diff --git a/test/coreprofile/no_servlet_class/src/main/java/cloud/piranha/test/coreprofile/no_servlet_class/EchoApplication.java b/test/coreprofile/no_servlet_class/src/main/java/cloud/piranha/test/coreprofile/no_servlet_class/EchoApplication.java
new file mode 100644
index 0000000000..6bc1632b46
--- /dev/null
+++ b/test/coreprofile/no_servlet_class/src/main/java/cloud/piranha/test/coreprofile/no_servlet_class/EchoApplication.java
@@ -0,0 +1,42 @@
+/*
+ * 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.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;
+ }
+}
diff --git a/test/coreprofile/no_servlet_class/src/main/java/cloud/piranha/test/coreprofile/no_servlet_class/EchoBean.java b/test/coreprofile/no_servlet_class/src/main/java/cloud/piranha/test/coreprofile/no_servlet_class/EchoBean.java
new file mode 100644
index 0000000000..45e749eff2
--- /dev/null
+++ b/test/coreprofile/no_servlet_class/src/main/java/cloud/piranha/test/coreprofile/no_servlet_class/EchoBean.java
@@ -0,0 +1,49 @@
+/*
+ * 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.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";
+ }
+}
diff --git a/test/coreprofile/no_servlet_class/src/main/webapp/WEB-INF/web.xml b/test/coreprofile/no_servlet_class/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000000..953bb4c7f4
--- /dev/null
+++ b/test/coreprofile/no_servlet_class/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,12 @@
+
+
+ cloud.piranha.test.coreprofile.no_servlet_class.EchoApplication
+
+
+ cloud.piranha.test.coreprofile.no_servlet_class.EchoApplication
+ /*
+
+
+ 30
+
+
diff --git a/test/coreprofile/no_servlet_class/src/test/java/cloud/piranha/test/coreprofile/no_servlet_class/EchoIT.java b/test/coreprofile/no_servlet_class/src/test/java/cloud/piranha/test/coreprofile/no_servlet_class/EchoIT.java
new file mode 100644
index 0000000000..81c990f52d
--- /dev/null
+++ b/test/coreprofile/no_servlet_class/src/test/java/cloud/piranha/test/coreprofile/no_servlet_class/EchoIT.java
@@ -0,0 +1,24 @@
+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());
+ }
+}
diff --git a/test/coreprofile/pom.xml b/test/coreprofile/pom.xml
index 595e90847a..3b14af65a7 100644
--- a/test/coreprofile/pom.xml
+++ b/test/coreprofile/pom.xml
@@ -33,5 +33,6 @@
arquillian
integration
json
+ no_servlet_class