You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2025/01/12 18:07:58 UTC
(sling-org-apache-sling-servlet-helpers) branch jakarta-2 updated: Implement new API based on Jakarta Servlet
This is an automated email from the ASF dual-hosted git repository.
cziegeler pushed a commit to branch jakarta-2
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-servlet-helpers.git
The following commit(s) were added to refs/heads/jakarta-2 by this push:
new 6609124 Implement new API based on Jakarta Servlet
6609124 is described below
commit 66091241d607c31ccdf34f359b8cb9baa8ed0ce9
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Sun Jan 12 19:07:52 2025 +0100
Implement new API based on Jakarta Servlet
---
pom.xml | 18 +-
.../sling/servlethelpers/JakartaByteArrayPart.java | 125 +++
...package-info.java => JakartaCookieSupport.java} | 36 +-
.../servlethelpers/JakartaResponseBodySupport.java | 110 ++
.../servlethelpers/MockJakartaHttpSession.java | 135 +++
.../MockJakartaRequestDispatcherFactory.java | 49 +
.../servlethelpers/MockJakartaServletContext.java | 323 ++++++
.../MockSlingJakartaHttpServletRequest.java | 1049 ++++++++++++++++++++
.../MockSlingJakartaHttpServletResponse.java | 296 ++++++
.../apache/sling/servlethelpers/package-info.java | 2 +-
.../JakartaRequestInfoServlet.java | 98 ++
.../internalrequests/MockServletResolver.java | 18 +
.../MockSlingRequestProcessor.java | 21 +
13 files changed, 2270 insertions(+), 10 deletions(-)
diff --git a/pom.xml b/pom.xml
index 528f09b..deb87d3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,9 +7,9 @@
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
-
+
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -28,7 +28,7 @@
</parent>
<artifactId>org.apache.sling.servlet-helpers</artifactId>
- <version>1.4.7-SNAPSHOT</version>
+ <version>2.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Apache Sling Servlet Helpers</name>
@@ -42,7 +42,7 @@
</scm>
<properties>
- <sling.java.version>8</sling.java.version>
+ <sling.java.version>17</sling.java.version>
<org.ops4j.pax.exam.version>4.13.3</org.ops4j.pax.exam.version>
<!-- To debug the pax process, override this with -D -->
<pax.vm.options>-Xmx512M</pax.vm.options>
@@ -60,6 +60,12 @@
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>jakarta.servlet</groupId>
+ <artifactId>jakarta.servlet-api</artifactId>
+ <version>6.0.0</version>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
@@ -68,13 +74,13 @@
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.api</artifactId>
- <version>2.25.4</version>
+ <version>3.0.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.engine</artifactId>
- <version>2.6.22</version>
+ <version>3.0.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
diff --git a/src/main/java/org/apache/sling/servlethelpers/JakartaByteArrayPart.java b/src/main/java/org/apache/sling/servlethelpers/JakartaByteArrayPart.java
new file mode 100644
index 0000000..21b6a81
--- /dev/null
+++ b/src/main/java/org/apache/sling/servlethelpers/JakartaByteArrayPart.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.servlethelpers;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+
+import jakarta.servlet.http.Part;
+
+/**
+ * Simple Part implementation backed by an in-memory byte array
+ * @since 1.10.0
+ *
+ */
+public class JakartaByteArrayPart implements Part {
+
+ /**
+ * Returns a Builder instance used to create a ByteArrayPart
+ *
+ * @return a new builder instance
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ private final byte[] content;
+ private final String name;
+
+ private JakartaByteArrayPart(byte[] content, String name) {
+ if (content == null) throw new IllegalArgumentException("content may not be null");
+
+ if (name == null || name.isEmpty()) throw new IllegalArgumentException("name may not be null or empty");
+
+ this.content = content;
+ this.name = name;
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ return new ByteArrayInputStream(content);
+ }
+
+ @Override
+ public String getContentType() {
+ return null;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getSubmittedFileName() {
+ return getName();
+ }
+
+ @Override
+ public long getSize() {
+ return content.length;
+ }
+
+ @Override
+ public void write(String fileName) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void delete() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getHeader(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Collection<String> getHeaders(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Collection<String> getHeaderNames() {
+ throw new UnsupportedOperationException();
+ }
+
+ public static class Builder {
+
+ private byte[] content;
+ private String name;
+
+ public Builder withContent(byte[] content) {
+ this.content = content;
+ return this;
+ }
+
+ public Builder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public JakartaByteArrayPart build() {
+ return new JakartaByteArrayPart(content, name);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/sling/servlethelpers/package-info.java b/src/main/java/org/apache/sling/servlethelpers/JakartaCookieSupport.java
similarity index 54%
copy from src/main/java/org/apache/sling/servlethelpers/package-info.java
copy to src/main/java/org/apache/sling/servlethelpers/JakartaCookieSupport.java
index 2bcdb37..319e965 100644
--- a/src/main/java/org/apache/sling/servlethelpers/package-info.java
+++ b/src/main/java/org/apache/sling/servlethelpers/JakartaCookieSupport.java
@@ -16,8 +16,38 @@
* specific language governing permissions and limitations
* under the License.
*/
+package org.apache.sling.servlethelpers;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import jakarta.servlet.http.Cookie;
+
/**
- * Mock implementation of selected Servlet-related Sling APIs.
+ * Manages cookies for request and response.
+ * @since 1.10.0
*/
-@org.osgi.annotation.versioning.Version("1.9")
-package org.apache.sling.servlethelpers;
+class JakartaCookieSupport {
+
+ private Map<String, Cookie> cookies = new LinkedHashMap<String, Cookie>();
+
+ public void addCookie(Cookie cookie) {
+ cookies.put(cookie.getName(), cookie);
+ }
+
+ public Cookie getCookie(String name) {
+ return cookies.get(name);
+ }
+
+ public Cookie[] getCookies() {
+ if (cookies.isEmpty()) {
+ return null;
+ } else {
+ return cookies.values().toArray(new Cookie[cookies.size()]);
+ }
+ }
+
+ public void reset() {
+ cookies.clear();
+ }
+}
diff --git a/src/main/java/org/apache/sling/servlethelpers/JakartaResponseBodySupport.java b/src/main/java/org/apache/sling/servlethelpers/JakartaResponseBodySupport.java
new file mode 100644
index 0000000..d880a0b
--- /dev/null
+++ b/src/main/java/org/apache/sling/servlethelpers/JakartaResponseBodySupport.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.servlethelpers;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+
+import jakarta.servlet.ServletOutputStream;
+import jakarta.servlet.WriteListener;
+import org.apache.commons.lang3.CharEncoding;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * Manage response body content.
+ * @since 1.10.0
+ */
+class JakartaResponseBodySupport {
+
+ private ByteArrayOutputStream outputStream;
+ private ServletOutputStream servletOutputStream;
+ private PrintWriter printWriter;
+
+ public JakartaResponseBodySupport() {
+ reset();
+ }
+
+ public void reset() {
+ outputStream = new ByteArrayOutputStream();
+ servletOutputStream = null;
+ printWriter = null;
+ }
+
+ public ServletOutputStream getOutputStream() {
+ if (servletOutputStream == null) {
+ servletOutputStream = new ServletOutputStream() {
+ @Override
+ public void write(int b) throws IOException {
+ outputStream.write(b);
+ }
+
+ @Override
+ public boolean isReady() {
+ return true;
+ }
+
+ @Override
+ public void setWriteListener(WriteListener writeListener) {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ return servletOutputStream;
+ }
+
+ public PrintWriter getWriter(String charset) {
+ if (printWriter == null) {
+ try {
+ printWriter = new PrintWriter(new OutputStreamWriter(getOutputStream(), defaultCharset(charset)));
+ } catch (UnsupportedEncodingException ex) {
+ throw new RuntimeException("Unsupported encoding: " + defaultCharset(charset), ex);
+ }
+ }
+ return printWriter;
+ }
+
+ public byte[] getOutput() {
+ if (printWriter != null) {
+ printWriter.flush();
+ }
+ if (servletOutputStream != null) {
+ try {
+ servletOutputStream.flush();
+ } catch (IOException ex) {
+ // ignore
+ }
+ }
+ return outputStream.toByteArray();
+ }
+
+ public String getOutputAsString(String charset) {
+ try {
+ return new String(getOutput(), defaultCharset(charset));
+ } catch (UnsupportedEncodingException ex) {
+ throw new RuntimeException("Unsupported encoding: " + defaultCharset(charset), ex);
+ }
+ }
+
+ private String defaultCharset(String charset) {
+ return StringUtils.defaultString(charset, CharEncoding.UTF_8);
+ }
+}
diff --git a/src/main/java/org/apache/sling/servlethelpers/MockJakartaHttpSession.java b/src/main/java/org/apache/sling/servlethelpers/MockJakartaHttpSession.java
new file mode 100644
index 0000000..3c95670
--- /dev/null
+++ b/src/main/java/org/apache/sling/servlethelpers/MockJakartaHttpSession.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.servlethelpers;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.http.HttpSession;
+import org.apache.commons.collections4.IteratorUtils;
+import org.osgi.annotation.versioning.ConsumerType;
+
+/**
+ * Mock {@link HttpSession} implementation.
+ * @since 1.10.0
+ */
+@ConsumerType
+public class MockJakartaHttpSession implements HttpSession {
+
+ private final ServletContext servletContext;
+ private final Map<String, Object> attributeMap = new HashMap<String, Object>();
+ private final String sessionID = UUID.randomUUID().toString();
+ private final long creationTime = System.currentTimeMillis();
+ private boolean invalidated = false;
+ private boolean isNew = true;
+ private int maxActiveInterval = 1800;
+
+ public MockJakartaHttpSession() {
+ this.servletContext = newMockJakartaServletContext();
+ }
+
+ protected MockJakartaServletContext newMockJakartaServletContext() {
+ return new MockJakartaServletContext();
+ }
+
+ @Override
+ public ServletContext getServletContext() {
+ return this.servletContext;
+ }
+
+ @Override
+ public Object getAttribute(final String name) {
+ checkInvalidatedState();
+ return this.attributeMap.get(name);
+ }
+
+ @Override
+ public Enumeration<String> getAttributeNames() {
+ checkInvalidatedState();
+ return IteratorUtils.asEnumeration(this.attributeMap.keySet().iterator());
+ }
+
+ @Override
+ public String getId() {
+ return this.sessionID;
+ }
+
+ @Override
+ public long getCreationTime() {
+ checkInvalidatedState();
+ return this.creationTime;
+ }
+
+ @Override
+ public void removeAttribute(final String name) {
+ checkInvalidatedState();
+ this.attributeMap.remove(name);
+ }
+
+ @Override
+ public void setAttribute(final String name, final Object value) {
+ checkInvalidatedState();
+ this.attributeMap.put(name, value);
+ }
+
+ @Override
+ public void invalidate() {
+ checkInvalidatedState();
+ this.invalidated = true;
+ }
+
+ private void checkInvalidatedState() {
+ if (invalidated) {
+ throw new IllegalStateException("Session is already invalidated.");
+ }
+ }
+
+ public boolean isInvalidated() {
+ return invalidated;
+ }
+
+ @Override
+ public boolean isNew() {
+ checkInvalidatedState();
+ return isNew;
+ }
+
+ public void setNew(boolean isNew) {
+ this.isNew = isNew;
+ }
+
+ @Override
+ public long getLastAccessedTime() {
+ checkInvalidatedState();
+ return creationTime;
+ }
+
+ @Override
+ public int getMaxInactiveInterval() {
+ return maxActiveInterval;
+ }
+
+ @Override
+ public void setMaxInactiveInterval(final int interval) {
+ this.maxActiveInterval = interval;
+ }
+}
diff --git a/src/main/java/org/apache/sling/servlethelpers/MockJakartaRequestDispatcherFactory.java b/src/main/java/org/apache/sling/servlethelpers/MockJakartaRequestDispatcherFactory.java
new file mode 100644
index 0000000..c5bae1e
--- /dev/null
+++ b/src/main/java/org/apache/sling/servlethelpers/MockJakartaRequestDispatcherFactory.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.servlethelpers;
+
+import jakarta.servlet.RequestDispatcher;
+import org.apache.sling.api.request.RequestDispatcherOptions;
+import org.apache.sling.api.resource.Resource;
+import org.osgi.annotation.versioning.ConsumerType;
+
+/**
+ * Interface to create a mock {@link RequestDispatcher} when calling the getRequestDispatcher methods
+ * on {@link MockSlingJakartaHttpServletRequest} instances.
+ * @since 1.10.0
+ */
+@ConsumerType
+public interface MockJakartaRequestDispatcherFactory {
+
+ /**
+ * Get request dispatcher for given path.
+ * @param path Path
+ * @param options Options. Null if no options are provided.
+ * @return Request dispatcher
+ */
+ RequestDispatcher getRequestDispatcher(String path, RequestDispatcherOptions options);
+
+ /**
+ * Get request dispatcher for given resource.
+ * @param resource Resource
+ * @param options Options. Null if no options are provided.
+ * @return Request dispatcher
+ */
+ RequestDispatcher getRequestDispatcher(Resource resource, RequestDispatcherOptions options);
+}
diff --git a/src/main/java/org/apache/sling/servlethelpers/MockJakartaServletContext.java b/src/main/java/org/apache/sling/servlethelpers/MockJakartaServletContext.java
new file mode 100644
index 0000000..4a0fae5
--- /dev/null
+++ b/src/main/java/org/apache/sling/servlethelpers/MockJakartaServletContext.java
@@ -0,0 +1,323 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.servlethelpers;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.EventListener;
+import java.util.Map;
+import java.util.Set;
+
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterRegistration;
+import jakarta.servlet.RequestDispatcher;
+import jakarta.servlet.Servlet;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRegistration;
+import jakarta.servlet.ServletRegistration.Dynamic;
+import jakarta.servlet.SessionCookieConfig;
+import jakarta.servlet.SessionTrackingMode;
+import jakarta.servlet.descriptor.JspConfigDescriptor;
+import org.osgi.annotation.versioning.ConsumerType;
+
+/**
+ * Mock {@link ServletContext} implementation.
+ * @since 1.10.0
+ */
+@ConsumerType
+public class MockJakartaServletContext implements ServletContext {
+
+ @Override
+ public String getMimeType(final String file) {
+ return "application/octet-stream";
+ }
+
+ // --- unsupported operations ---
+ @Override
+ public Object getAttribute(final String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Enumeration<String> getAttributeNames() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ServletContext getContext(final String uriPath) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getContextPath() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getInitParameter(final String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Enumeration<String> getInitParameterNames() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getMajorVersion() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getMinorVersion() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public RequestDispatcher getNamedDispatcher(final String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getRealPath(final String pPath) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public RequestDispatcher getRequestDispatcher(final String path) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public URL getResource(final String pPath) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public InputStream getResourceAsStream(final String path) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<String> getResourcePaths(final String path) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getServerInfo() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getServletContextName() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void log(final String msg) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void log(final String msg, final Throwable throwable) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeAttribute(final String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setAttribute(final String name, final Object object) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getEffectiveMajorVersion() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getEffectiveMinorVersion() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean setInitParameter(final String name, final String value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Dynamic addServlet(final String servletName, final String className) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Dynamic addServlet(final String servletName, final Servlet servlet) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Dynamic addServlet(final String servletName, final Class<? extends Servlet> servletClass) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T extends Servlet> T createServlet(final Class<T> clazz) throws ServletException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ServletRegistration getServletRegistration(final String servletName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map<String, ? extends ServletRegistration> getServletRegistrations() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public FilterRegistration.Dynamic addFilter(final String filterName, final String className) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public FilterRegistration.Dynamic addFilter(final String filterName, final Filter filter) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public FilterRegistration.Dynamic addFilter(final String filterName, final Class<? extends Filter> filterClass) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T extends Filter> T createFilter(final Class<T> clazz) throws ServletException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public FilterRegistration getFilterRegistration(final String filterName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SessionCookieConfig getSessionCookieConfig() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setSessionTrackingModes(final Set<SessionTrackingMode> sessionTrackingModes) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void addListener(final String pClassName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T extends EventListener> void addListener(final T listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void addListener(final Class<? extends EventListener> listenerClass) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T extends EventListener> T createListener(final Class<T> clazz) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public JspConfigDescriptor getJspConfigDescriptor() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ClassLoader getClassLoader() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void declareRoles(final String... roleNames) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getVirtualServerName() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Dynamic addJspFile(String servletName, String jspFile) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getRequestCharacterEncoding() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getResponseCharacterEncoding() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getSessionTimeout() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setRequestCharacterEncoding(String encoding) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setResponseCharacterEncoding(String encoding) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setSessionTimeout(int sessionTimeout) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/main/java/org/apache/sling/servlethelpers/MockSlingJakartaHttpServletRequest.java b/src/main/java/org/apache/sling/servlethelpers/MockSlingJakartaHttpServletRequest.java
new file mode 100644
index 0000000..517b0d1
--- /dev/null
+++ b/src/main/java/org/apache/sling/servlethelpers/MockSlingJakartaHttpServletRequest.java
@@ -0,0 +1,1049 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.servlethelpers;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.ListResourceBundle;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.ResourceBundle;
+
+import jakarta.servlet.AsyncContext;
+import jakarta.servlet.DispatcherType;
+import jakarta.servlet.ReadListener;
+import jakarta.servlet.RequestDispatcher;
+import jakarta.servlet.ServletConnection;
+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.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
+import jakarta.servlet.http.HttpUpgradeHandler;
+import jakarta.servlet.http.Part;
+import org.apache.commons.collections4.IteratorUtils;
+import org.apache.commons.lang3.CharEncoding;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.api.SlingJakartaHttpServletRequest;
+import org.apache.sling.api.adapter.SlingAdaptable;
+import org.apache.sling.api.request.RequestDispatcherOptions;
+import org.apache.sling.api.request.RequestParameter;
+import org.apache.sling.api.request.RequestParameterMap;
+import org.apache.sling.api.request.RequestPathInfo;
+import org.apache.sling.api.request.RequestProgressTracker;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.servlets.HttpConstants;
+import org.osgi.annotation.versioning.ConsumerType;
+
+import static org.apache.sling.servlethelpers.MockSlingHttpServletResponse.CHARSET_SEPARATOR;
+
+/**
+ * Mock {@link SlingJakartaHttpServletRequest} implementation.
+ * @since 1.10.0
+ */
+@ConsumerType
+public class MockSlingJakartaHttpServletRequest extends SlingAdaptable implements SlingJakartaHttpServletRequest {
+
+ private final ResourceResolver resourceResolver;
+ private final RequestPathInfo requestPathInfo;
+ private Map<String, Object> attributeMap = new HashMap<>();
+ private Map<String, MockRequestParameter[]> parameterMap = new LinkedHashMap<>();
+ private HttpSession session;
+ private Resource resource;
+ private String authType;
+ private String contextPath = "";
+ private String queryString;
+ private String scheme = "http";
+ private String serverName = "localhost";
+ private int serverPort = 80;
+ private String servletPath = StringUtils.EMPTY;
+ private String pathInfo = null;
+ private String method = HttpConstants.METHOD_GET;
+ private final HeaderSupport headerSupport = new HeaderSupport();
+ private final JakartaCookieSupport cookieSupport = new JakartaCookieSupport();
+ private String contentType;
+ private String characterEncoding;
+ private byte[] content;
+ private String remoteUser;
+ private String remoteAddr;
+ private String remoteHost;
+ private int remotePort;
+ private Locale locale = Locale.US;
+ private boolean getInputStreamCalled;
+ private boolean getReaderCalled;
+ private List<Part> parts = new ArrayList<>();
+
+ private MockJakartaRequestDispatcherFactory requestDispatcherFactory;
+ private String responseContentType;
+
+ protected static final ResourceBundle EMPTY_RESOURCE_BUNDLE = new ListResourceBundle() {
+ @Override
+ protected Object[][] getContents() {
+ return new Object[0][0];
+ }
+ };
+
+ /**
+ * @param resourceResolver Resource resolver
+ */
+ public MockSlingJakartaHttpServletRequest(ResourceResolver resourceResolver) {
+ this.resourceResolver = resourceResolver;
+ this.requestPathInfo = newMockRequestPathInfo();
+ }
+
+ protected MockJakartaHttpSession newMockJakartaHttpSession() {
+ return new MockJakartaHttpSession();
+ }
+
+ protected MockRequestPathInfo newMockRequestPathInfo() {
+ return new MockRequestPathInfo(this.resourceResolver);
+ }
+
+ @Override
+ public ResourceResolver getResourceResolver() {
+ return this.resourceResolver;
+ }
+
+ @Override
+ public HttpSession getSession() {
+ return getSession(true);
+ }
+
+ @Override
+ public HttpSession getSession(boolean create) {
+ if (this.session == null && create) {
+ this.session = newMockJakartaHttpSession();
+ }
+ return this.session;
+ }
+
+ @Override
+ public RequestPathInfo getRequestPathInfo() {
+ return this.requestPathInfo;
+ }
+
+ @Override
+ public Object getAttribute(String name) {
+ return this.attributeMap.get(name);
+ }
+
+ @Override
+ public Enumeration<String> getAttributeNames() {
+ return IteratorUtils.asEnumeration(this.attributeMap.keySet().iterator());
+ }
+
+ @Override
+ public void removeAttribute(String name) {
+ this.attributeMap.remove(name);
+ }
+
+ @Override
+ public void setAttribute(String name, Object object) {
+ this.attributeMap.put(name, object);
+ }
+
+ @Override
+ public Resource getResource() {
+ return this.resource;
+ }
+
+ public void setResource(Resource resource) {
+ this.resource = resource;
+ }
+
+ @Override
+ public String getParameter(String name) {
+ MockRequestParameter[] params = this.parameterMap.get(name);
+ if (params != null && params.length > 0) {
+ return params[0].getString();
+ }
+ return null;
+ }
+
+ @Override
+ public Map<String, String[]> getParameterMap() {
+ LinkedHashMap<String, String[]> result = new LinkedHashMap<>();
+ for (Entry<String, MockRequestParameter[]> entry : this.parameterMap.entrySet()) {
+ MockRequestParameter[] values = entry.getValue();
+ String[] resultValues = new String[values.length];
+ for (int i = 0; i < values.length; i++) {
+ resultValues[i] = values[i].getString();
+ }
+ result.put(entry.getKey(), resultValues);
+ }
+
+ return result;
+ }
+
+ @Override
+ public Enumeration<String> getParameterNames() {
+ return IteratorUtils.asEnumeration(this.parameterMap.keySet().iterator());
+ }
+
+ @Override
+ public String[] getParameterValues(String name) { // NOPMD
+ MockRequestParameter[] param = this.parameterMap.get(name);
+ if (param != null) {
+ String[] values = new String[param.length];
+ for (int i = 0; i < param.length; i++) {
+ values[i] = param[i].getString();
+ }
+ return values;
+ }
+ return null; // NOPMD
+ }
+
+ /**
+ * @param parameterMap Map of parameters
+ */
+ public void setParameterMap(Map<String, Object> parameterMap) {
+ this.parameterMap.clear();
+ for (Map.Entry<String, Object> entry : parameterMap.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+ if (value instanceof String[]) {
+ String[] array = (String[]) value;
+ MockRequestParameter[] values = new MockRequestParameter[array.length];
+ for (int i = 0; i < array.length; i++) {
+ values[i] = new MockRequestParameter(key, array[i]);
+ }
+ this.parameterMap.put(key, values);
+ } else if (value instanceof MockRequestParameter[]) {
+ this.parameterMap.put(key, (MockRequestParameter[]) value);
+ } else if (value != null) {
+ this.addRequestParameter(key, value.toString());
+ } else {
+ this.parameterMap.put(key, null);
+ }
+ }
+ try {
+ this.queryString = formatQueryString(this.parameterMap);
+ } catch (UnsupportedEncodingException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private static String formatQueryString(Map<String, MockRequestParameter[]> map)
+ throws UnsupportedEncodingException {
+ StringBuilder querystring = new StringBuilder();
+ for (Map.Entry<String, MockRequestParameter[]> entry : map.entrySet()) {
+ if (entry.getValue() != null) {
+ formatQueryStringParameter(querystring, entry);
+ }
+ }
+ return querystring.length() > 0 ? querystring.toString() : null;
+ }
+
+ private static void formatQueryStringParameter(
+ StringBuilder querystring, Map.Entry<String, MockRequestParameter[]> entry)
+ throws UnsupportedEncodingException {
+ for (MockRequestParameter value : entry.getValue()) {
+ if (querystring.length() != 0) {
+ querystring.append('&');
+ }
+ querystring.append(URLEncoder.encode(entry.getKey(), CharEncoding.UTF_8));
+ querystring.append('=');
+ if (value.getString() != null) {
+ querystring.append(URLEncoder.encode(value.getString(), CharEncoding.UTF_8));
+ }
+ }
+ }
+
+ @Override
+ public Locale getLocale() {
+ return locale;
+ }
+
+ /**
+ * @param loc Request locale
+ */
+ public void setLocale(Locale loc) {
+ this.locale = loc;
+ }
+
+ @Override
+ public String getContextPath() {
+ return this.contextPath;
+ }
+
+ /**
+ * @param contextPath Webapp context path
+ */
+ public void setContextPath(String contextPath) {
+ this.contextPath = contextPath;
+ }
+
+ /**
+ * @param queryString Query string (with proper URL encoding)
+ */
+ public void setQueryString(String queryString) {
+ this.queryString = queryString;
+ try {
+ parseQueryString(this.parameterMap, this.queryString);
+ } catch (UnsupportedEncodingException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private void parseQueryString(Map<String, MockRequestParameter[]> map, String query)
+ throws UnsupportedEncodingException {
+ Map<String, List<String>> queryPairs = new LinkedHashMap<>();
+ String[] pairs = query.split("&");
+ for (String pair : pairs) {
+ int idx = pair.indexOf('=');
+ String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), CharEncoding.UTF_8) : pair;
+ if (!queryPairs.containsKey(key)) {
+ queryPairs.put(key, new ArrayList<String>());
+ }
+ String value = idx > 0 && pair.length() > idx + 1
+ ? URLDecoder.decode(pair.substring(idx + 1), CharEncoding.UTF_8)
+ : null;
+ queryPairs.get(key).add(value);
+ }
+ map.clear();
+ for (Map.Entry<String, List<String>> entry : queryPairs.entrySet()) {
+ List<String> valueList = entry.getValue();
+ int numEntries = valueList.size();
+ MockRequestParameter[] values = new MockRequestParameter[numEntries];
+ for (int i = 0; i < numEntries; i++) {
+ values[i] = new MockRequestParameter(entry.getKey(), valueList.get(i));
+ }
+ map.put(entry.getKey(), values);
+ }
+ }
+
+ @Override
+ public String getQueryString() {
+ return this.queryString;
+ }
+
+ @Override
+ public String getScheme() {
+ return this.scheme;
+ }
+
+ public void setScheme(String scheme) {
+ this.scheme = scheme;
+ }
+
+ @Override
+ public String getServerName() {
+ return this.serverName;
+ }
+
+ public void setServerName(String serverName) {
+ this.serverName = serverName;
+ }
+
+ @Override
+ public int getServerPort() {
+ return this.serverPort;
+ }
+
+ public void setServerPort(int serverPort) {
+ this.serverPort = serverPort;
+ }
+
+ @Override
+ public boolean isSecure() {
+ return StringUtils.equals("https", getScheme());
+ }
+
+ @Override
+ public String getMethod() {
+ return this.method;
+ }
+
+ public void setMethod(String method) {
+ this.method = method;
+ }
+
+ @Override
+ public long getDateHeader(String name) {
+ return headerSupport.getDateHeader(name);
+ }
+
+ @Override
+ public String getHeader(String name) {
+ return headerSupport.getHeader(name);
+ }
+
+ @Override
+ public Enumeration<String> getHeaderNames() {
+ return HeaderSupport.toEnumeration(headerSupport.getHeaderNames());
+ }
+
+ @Override
+ public Enumeration<String> getHeaders(String name) {
+ return HeaderSupport.toEnumeration(headerSupport.getHeaders(name));
+ }
+
+ @Override
+ public int getIntHeader(String name) {
+ return headerSupport.getIntHeader(name);
+ }
+
+ /**
+ * Add header, keep existing ones with same name.
+ *
+ * @param name Header name
+ * @param value Header value
+ */
+ public void addHeader(String name, String value) {
+ headerSupport.addHeader(name, value);
+ }
+
+ /**
+ * Add header, keep existing ones with same name.
+ *
+ * @param name Header name
+ * @param value Header value
+ */
+ public void addIntHeader(String name, int value) {
+ headerSupport.addIntHeader(name, value);
+ }
+
+ /**
+ * Add header, keep existing ones with same name.
+ *
+ * @param name Header name
+ * @param date Header value
+ */
+ public void addDateHeader(String name, long date) {
+ headerSupport.addDateHeader(name, date);
+ }
+
+ /**
+ * Set header, overwrite existing ones with same name.
+ *
+ * @param name Header name
+ * @param value Header value
+ */
+ public void setHeader(String name, String value) {
+ headerSupport.setHeader(name, value);
+ }
+
+ /**
+ * Set header, overwrite existing ones with same name.
+ *
+ * @param name Header name
+ * @param value Header value
+ */
+ public void setIntHeader(String name, int value) {
+ headerSupport.setIntHeader(name, value);
+ }
+
+ /**
+ * Set header, overwrite existing ones with same name.
+ *
+ * @param name Header name
+ * @param date Header value
+ */
+ public void setDateHeader(String name, long date) {
+ headerSupport.setDateHeader(name, date);
+ }
+
+ @Override
+ public Cookie getCookie(String name) {
+ return cookieSupport.getCookie(name);
+ }
+
+ @Override
+ public Cookie[] getCookies() {
+ return cookieSupport.getCookies();
+ }
+
+ /**
+ * Set cookie
+ *
+ * @param cookie Cookie
+ */
+ public void addCookie(Cookie cookie) {
+ cookieSupport.addCookie(cookie);
+ }
+
+ @Override
+ public ResourceBundle getResourceBundle(Locale locale) {
+ return getResourceBundle(null, locale);
+ }
+
+ @Override
+ public ResourceBundle getResourceBundle(String baseName, Locale locale) {
+ return EMPTY_RESOURCE_BUNDLE;
+ }
+
+ @Override
+ public RequestParameter getRequestParameter(String name) {
+ MockRequestParameter[] params = this.parameterMap.get(name);
+ if (params != null && params.length > 0) {
+ return params[0];
+ }
+ return null;
+ }
+
+ @Override
+ public RequestParameterMap getRequestParameterMap() {
+ MockRequestParameterMap map = new MockRequestParameterMap();
+ for (Map.Entry<String, String[]> entry : getParameterMap().entrySet()) {
+ map.put(entry.getKey(), getRequestParameters(entry.getKey()));
+ }
+ return map;
+ }
+
+ @Override
+ public RequestParameter[] getRequestParameters(String name) {
+ return this.parameterMap.get(name);
+ }
+
+ // part of Sling API 2.7
+ public List<RequestParameter> getRequestParameterList() {
+ List<RequestParameter> params = new ArrayList<>();
+ for (RequestParameter[] requestParameters : getRequestParameterMap().values()) {
+ params.addAll(Arrays.asList(requestParameters));
+ }
+ return params;
+ }
+
+ /**
+ * Add a request parameter that consists of a simple name/value pair. This
+ * emulates a simple form field.
+ *
+ * @param name field name
+ * @param value field value
+ */
+ public void addRequestParameter(String name, String value) {
+ MockRequestParameter mockRequestParameter = new MockRequestParameter(name, value);
+ addMockRequestParameter(name, mockRequestParameter);
+ }
+
+ /**
+ * Add a request parameter that emulates a file upload field.
+ *
+ * @param name field name
+ * @param content file content
+ * @param contentType mime type of content in the field
+ */
+ public void addRequestParameter(String name, byte[] content, String contentType) {
+ MockRequestParameter mockRequestParameter = new MockRequestParameter(name, content, contentType);
+ addMockRequestParameter(name, mockRequestParameter);
+ }
+
+ /**
+ * Add a request parameter that emulates a file upload field with a filename
+ * associated with it.
+ *
+ * @param name field name
+ * @param content file content
+ * @param contentType mime type of content in the field
+ * @param filename filename associated with content
+ */
+ public void addRequestParameter(String name, byte[] content, String contentType, String filename) {
+ MockRequestParameter mockRequestParameter = new MockRequestParameter(name, content, contentType, filename);
+ addMockRequestParameter(name, mockRequestParameter);
+ }
+
+ private void addMockRequestParameter(String name, MockRequestParameter mockRequestParameter) {
+ if (this.parameterMap.containsKey(name)) {
+ List<MockRequestParameter> list = new ArrayList<>(Arrays.asList(this.parameterMap.get(name)));
+ list.add(mockRequestParameter);
+ this.parameterMap.put(name, list.toArray(new MockRequestParameter[0]));
+ } else {
+ this.parameterMap.put(name, new MockRequestParameter[] {mockRequestParameter});
+ }
+ }
+
+ @Override
+ public String getCharacterEncoding() {
+ return this.characterEncoding;
+ }
+
+ @Override
+ public void setCharacterEncoding(String charset) {
+ this.characterEncoding = charset;
+ }
+
+ @Override
+ public String getContentType() {
+ if (this.contentType == null) {
+ return null;
+ } else {
+ return this.contentType
+ + (StringUtils.isNotBlank(characterEncoding) ? CHARSET_SEPARATOR + characterEncoding : "");
+ }
+ }
+
+ public void setContentType(String type) {
+ this.contentType = type;
+ if (StringUtils.contains(this.contentType, CHARSET_SEPARATOR)) {
+ this.characterEncoding = StringUtils.substringAfter(this.contentType, CHARSET_SEPARATOR);
+ this.contentType = StringUtils.substringBefore(this.contentType, CHARSET_SEPARATOR);
+ }
+ }
+
+ @Override
+ public ServletInputStream getInputStream() {
+ if (getReaderCalled) {
+ throw new IllegalStateException();
+ }
+ getInputStreamCalled = true;
+ return new ServletInputStream() {
+ private final InputStream is =
+ content == null ? new ByteArrayInputStream(new byte[0]) : new ByteArrayInputStream(content);
+
+ @Override
+ public int read() throws IOException {
+ return is.read();
+ }
+
+ @Override
+ public boolean isReady() {
+ return true;
+ }
+
+ @Override
+ public boolean isFinished() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setReadListener(ReadListener readListener) {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ @Override
+ public int getContentLength() {
+ if (content == null) {
+ return 0;
+ }
+ return content.length;
+ }
+
+ public void setContent(byte[] content) {
+ this.content = content;
+ }
+
+ @Override
+ public RequestDispatcher getRequestDispatcher(String path) {
+ if (requestDispatcherFactory == null) {
+ throw new IllegalStateException(
+ "Please provdide a MockRequestDispatcherFactory (setRequestDispatcherFactory).");
+ }
+ return requestDispatcherFactory.getRequestDispatcher(path, null);
+ }
+
+ @Override
+ public RequestDispatcher getRequestDispatcher(String path, RequestDispatcherOptions options) {
+ if (requestDispatcherFactory == null) {
+ throw new IllegalStateException(
+ "Please provdide a MockRequestDispatcherFactory (setRequestDispatcherFactory).");
+ }
+ return requestDispatcherFactory.getRequestDispatcher(path, options);
+ }
+
+ @Override
+ public RequestDispatcher getRequestDispatcher(Resource resource) {
+ if (requestDispatcherFactory == null) {
+ throw new IllegalStateException(
+ "Please provdide a MockRequestDispatcherFactory (setRequestDispatcherFactory).");
+ }
+ return requestDispatcherFactory.getRequestDispatcher(resource, null);
+ }
+
+ @Override
+ public RequestDispatcher getRequestDispatcher(Resource resource, RequestDispatcherOptions options) {
+ if (requestDispatcherFactory == null) {
+ throw new IllegalStateException(
+ "Please provdide a MockRequestDispatcherFactory (setRequestDispatcherFactory).");
+ }
+ return requestDispatcherFactory.getRequestDispatcher(resource, options);
+ }
+
+ public void setRequestDispatcherFactory(MockJakartaRequestDispatcherFactory requestDispatcherFactory) {
+ this.requestDispatcherFactory = requestDispatcherFactory;
+ }
+
+ @Override
+ public String getRemoteUser() {
+ return remoteUser;
+ }
+
+ public void setRemoteUser(String remoteUser) {
+ this.remoteUser = remoteUser;
+ }
+
+ @Override
+ public String getRemoteAddr() {
+ return remoteAddr;
+ }
+
+ public void setRemoteAddr(String remoteAddr) {
+ this.remoteAddr = remoteAddr;
+ }
+
+ @Override
+ public String getRemoteHost() {
+ return remoteHost;
+ }
+
+ public void setRemoteHost(String remoteHost) {
+ this.remoteHost = remoteHost;
+ }
+
+ @Override
+ public int getRemotePort() {
+ return remotePort;
+ }
+
+ public void setRemotePort(int remotePort) {
+ this.remotePort = remotePort;
+ }
+
+ @Override
+ public String getServletPath() {
+ return this.servletPath;
+ }
+
+ public void setServletPath(String servletPath) {
+ this.servletPath = servletPath;
+ }
+
+ @Override
+ public String getPathInfo() {
+ if (this.pathInfo != null) {
+ return this.pathInfo;
+ }
+
+ RequestPathInfo requestPathInfo = this.getRequestPathInfo();
+
+ if (StringUtils.isEmpty(requestPathInfo.getResourcePath())) {
+ return null;
+ }
+
+ StringBuilder pathInfo = new StringBuilder();
+
+ pathInfo.append(requestPathInfo.getResourcePath());
+
+ if (StringUtils.isNotEmpty(requestPathInfo.getSelectorString())) {
+ pathInfo.append('.');
+ pathInfo.append(requestPathInfo.getSelectorString());
+ }
+
+ if (StringUtils.isNotEmpty(requestPathInfo.getExtension())) {
+ pathInfo.append('.');
+ pathInfo.append(requestPathInfo.getExtension());
+ }
+
+ if (StringUtils.isNotEmpty(requestPathInfo.getSuffix())) {
+ pathInfo.append(requestPathInfo.getSuffix());
+ }
+
+ return pathInfo.toString();
+ }
+
+ public void setPathInfo(String pathInfo) {
+ this.pathInfo = pathInfo;
+ }
+
+ @Override
+ public String getRequestURI() {
+ StringBuilder requestUri = new StringBuilder();
+
+ if (StringUtils.isNotEmpty(this.getServletPath())) {
+ requestUri.append(this.getServletPath());
+ }
+
+ if (StringUtils.isNotEmpty(this.getPathInfo())) {
+ requestUri.append(this.getPathInfo());
+ }
+
+ if (StringUtils.isEmpty(requestUri)) {
+ return "/";
+ } else {
+ return requestUri.toString();
+ }
+ }
+
+ @Override
+ public StringBuffer getRequestURL() {
+ StringBuffer requestUrl = new StringBuffer();
+
+ requestUrl.append(this.getScheme());
+ requestUrl.append("://");
+ requestUrl.append(getServerName());
+ if ((StringUtils.equals(this.getScheme(), "http") && this.getServerPort() != 80)
+ || (StringUtils.equals(this.getScheme(), "https") && this.getServerPort() != 443)) {
+ requestUrl.append(':');
+ requestUrl.append(getServerPort());
+ }
+ requestUrl.append(getRequestURI());
+
+ return requestUrl;
+ }
+
+ @Override
+ public String getAuthType() {
+ return this.authType;
+ }
+
+ public void setAuthType(String authType) {
+ this.authType = authType;
+ }
+
+ @Override
+ public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+ return AdaptableUtil.adaptToWithoutCaching(this, type);
+ }
+
+ @Override
+ public String getResponseContentType() {
+ return responseContentType;
+ }
+
+ public void setResponseContentType(String responseContentType) {
+ this.responseContentType = responseContentType;
+ }
+
+ @Override
+ public Enumeration<String> getResponseContentTypes() {
+ return Collections.enumeration(Collections.singleton(responseContentType));
+ }
+
+ @Override
+ public BufferedReader getReader() {
+ if (getInputStreamCalled) {
+ throw new IllegalStateException();
+ }
+ getReaderCalled = true;
+ if (this.content == null) {
+ return new BufferedReader(new StringReader(""));
+ } else {
+ String content;
+ try {
+ if (characterEncoding == null) {
+ content = new String(this.content, Charset.defaultCharset());
+ } else {
+ content = new String(this.content, characterEncoding);
+ }
+ } catch (UnsupportedEncodingException e) {
+ content = new String(this.content, Charset.defaultCharset());
+ }
+ return new BufferedReader(new StringReader(content));
+ }
+ }
+
+ @Override
+ public RequestProgressTracker getRequestProgressTracker() {
+ return new MockRequestProgressTracker();
+ }
+
+ public void addPart(Part part) {
+ if (part == null) throw new IllegalArgumentException("part may not be null");
+ this.parts.add(part);
+ }
+
+ @Override
+ public Collection<Part> getParts() {
+ return parts;
+ }
+
+ @Override
+ public Part getPart(String name) {
+ return parts.stream().filter(p -> p.getName().equals(name)).findFirst().orElse(null);
+ }
+
+ @Override
+ public Principal getUserPrincipal() {
+ Principal principal = null;
+ // always return null for anonymous user
+ final String userid = getRemoteUser();
+ if (userid != null) {
+ ResourceResolver rr = getResourceResolver();
+ if (rr != null) {
+ principal = rr.adaptTo(Principal.class);
+ }
+
+ if (principal == null) {
+ // fallback to the userid
+ principal = () -> userid;
+ }
+ }
+
+ return principal;
+ }
+
+ // --- unsupported operations ---
+
+ @Override
+ public String getPathTranslated() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getRequestedSessionId() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isRequestedSessionIdFromCookie() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isRequestedSessionIdFromURL() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isRequestedSessionIdValid() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isUserInRole(String role) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getLocalAddr() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getLocalName() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getLocalPort() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Enumeration<Locale> getLocales() {
+ return Collections.enumeration(Collections.singleton(getLocale()));
+ }
+
+ @Override
+ public String getProtocol() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean authenticate(HttpServletResponse response) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void login(String pUsername, String password) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void logout() throws ServletException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ServletContext getServletContext() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public AsyncContext startAsync() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isAsyncStarted() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isAsyncSupported() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public AsyncContext getAsyncContext() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public DispatcherType getDispatcherType() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String changeSessionId() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) throws IOException, ServletException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long getContentLengthLong() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getProtocolRequestId() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getRequestId() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ServletConnection getServletConnection() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/main/java/org/apache/sling/servlethelpers/MockSlingJakartaHttpServletResponse.java b/src/main/java/org/apache/sling/servlethelpers/MockSlingJakartaHttpServletResponse.java
new file mode 100644
index 0000000..96d4c9f
--- /dev/null
+++ b/src/main/java/org/apache/sling/servlethelpers/MockSlingJakartaHttpServletResponse.java
@@ -0,0 +1,296 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.servlethelpers;
+
+import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.Locale;
+
+import jakarta.servlet.ServletOutputStream;
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.api.SlingJakartaHttpServletResponse;
+import org.apache.sling.api.adapter.SlingAdaptable;
+import org.osgi.annotation.versioning.ConsumerType;
+
+/**
+ * Mock {@link SlingJakartaHttpServletResponse} implementation.
+ * @since 1.10.0
+ */
+@ConsumerType
+public class MockSlingJakartaHttpServletResponse extends SlingAdaptable implements SlingJakartaHttpServletResponse {
+
+ static final String CHARSET_SEPARATOR = ";charset=";
+
+ private String contentType;
+ private String characterEncoding;
+ private int contentLength;
+ private int status = HttpServletResponse.SC_OK;
+ private String statusMessage;
+ private int bufferSize = 1024 * 8;
+ private boolean isCommitted;
+ private Locale locale = Locale.US;
+ private final HeaderSupport headerSupport = new HeaderSupport();
+ private final JakartaResponseBodySupport bodySupport = new JakartaResponseBodySupport();
+ private final JakartaCookieSupport cookieSupport = new JakartaCookieSupport();
+
+ @Override
+ public String getContentType() {
+ if (this.contentType == null) {
+ return null;
+ } else {
+ return this.contentType
+ + (StringUtils.isNotBlank(characterEncoding) ? CHARSET_SEPARATOR + characterEncoding : "");
+ }
+ }
+
+ @Override
+ public void setContentType(String type) {
+ this.contentType = type;
+ if (StringUtils.contains(this.contentType, CHARSET_SEPARATOR)) {
+ this.characterEncoding = StringUtils.substringAfter(this.contentType, CHARSET_SEPARATOR);
+ this.contentType = StringUtils.substringBefore(this.contentType, CHARSET_SEPARATOR);
+ }
+ }
+
+ @Override
+ public void setCharacterEncoding(String charset) {
+ this.characterEncoding = charset;
+ }
+
+ @Override
+ public String getCharacterEncoding() {
+ return this.characterEncoding;
+ }
+
+ @Override
+ public void setContentLength(int len) {
+ this.contentLength = len;
+ }
+
+ public int getContentLength() {
+ return this.contentLength;
+ }
+
+ @Override
+ public void setStatus(int sc) {
+ this.status = sc;
+ }
+
+ @Override
+ public int getStatus() {
+ return this.status;
+ }
+
+ @Override
+ public void sendError(int sc, String msg) {
+ setStatus(sc);
+ this.statusMessage = msg;
+ }
+
+ @Override
+ public void sendError(int sc) {
+ setStatus(sc);
+ }
+
+ @Override
+ public void sendRedirect(String location) {
+ setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
+ setHeader("Location", location);
+ }
+
+ @Override
+ public void addHeader(String name, String value) {
+ headerSupport.addHeader(name, value);
+ }
+
+ @Override
+ public void addIntHeader(String name, int value) {
+ headerSupport.addIntHeader(name, value);
+ }
+
+ @Override
+ public void addDateHeader(String name, long date) {
+ headerSupport.addDateHeader(name, date);
+ }
+
+ @Override
+ public void setHeader(String name, String value) {
+ headerSupport.setHeader(name, value);
+ }
+
+ @Override
+ public void setIntHeader(String name, int value) {
+ headerSupport.setIntHeader(name, value);
+ }
+
+ @Override
+ public void setDateHeader(String name, long date) {
+ headerSupport.setDateHeader(name, date);
+ }
+
+ @Override
+ public boolean containsHeader(String name) {
+ return headerSupport.containsHeader(name);
+ }
+
+ @Override
+ public String getHeader(String name) {
+ return headerSupport.getHeader(name);
+ }
+
+ @Override
+ public Collection<String> getHeaders(String name) {
+ return headerSupport.getHeaders(name);
+ }
+
+ @Override
+ public Collection<String> getHeaderNames() {
+ return headerSupport.getHeaderNames();
+ }
+
+ @Override
+ public PrintWriter getWriter() {
+ return bodySupport.getWriter(getCharacterEncoding());
+ }
+
+ @Override
+ public ServletOutputStream getOutputStream() {
+ return bodySupport.getOutputStream();
+ }
+
+ @Override
+ public void reset() {
+ if (isCommitted()) {
+ throw new IllegalStateException("Response already committed.");
+ }
+ bodySupport.reset();
+ headerSupport.reset();
+ cookieSupport.reset();
+ status = HttpServletResponse.SC_OK;
+ contentLength = 0;
+ }
+
+ @Override
+ public void resetBuffer() {
+ if (isCommitted()) {
+ throw new IllegalStateException("Response already committed.");
+ }
+ bodySupport.reset();
+ }
+
+ @Override
+ public int getBufferSize() {
+ return this.bufferSize;
+ }
+
+ @Override
+ public void setBufferSize(int size) {
+ this.bufferSize = size;
+ }
+
+ @Override
+ public void flushBuffer() {
+ isCommitted = true;
+ }
+
+ @Override
+ public boolean isCommitted() {
+ return isCommitted;
+ }
+
+ public byte[] getOutput() {
+ return bodySupport.getOutput();
+ }
+
+ public String getOutputAsString() {
+ return bodySupport.getOutputAsString(getCharacterEncoding());
+ }
+
+ @Override
+ public void addCookie(Cookie cookie) {
+ cookieSupport.addCookie(cookie);
+ }
+
+ /**
+ * Get cookie
+ * @param name Cookie name
+ * @return Cookie or null
+ */
+ public Cookie getCookie(String name) {
+ return cookieSupport.getCookie(name);
+ }
+
+ /**
+ * Get cookies
+ * @return Cookies array or null if no cookie defined
+ */
+ public Cookie[] getCookies() {
+ return cookieSupport.getCookies();
+ }
+
+ @Override
+ public Locale getLocale() {
+ return locale;
+ }
+
+ @Override
+ public void setLocale(Locale loc) {
+ this.locale = loc;
+ }
+
+ /**
+ * @return status message that was set using {@link #setStatus(int, String)} or {@link #sendError(int, String)}
+ */
+ public String getStatusMessage() {
+ return statusMessage;
+ }
+
+ /**
+ * @deprecated As of package version 1.6, use {@link #getStatusMessage()} instead.
+ *
+ * @return status message that was set using {@link #setStatus(int, String)} or {@link #sendError(int, String)}
+ */
+ @Deprecated
+ public String geStatusMessage() {
+ return this.getStatusMessage();
+ }
+
+ @Override
+ public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+ return AdaptableUtil.adaptToWithoutCaching(this, type);
+ }
+
+ // --- unsupported operations ---
+ @Override
+ public String encodeRedirectURL(String url) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String encodeURL(String url) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setContentLengthLong(long len) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/main/java/org/apache/sling/servlethelpers/package-info.java b/src/main/java/org/apache/sling/servlethelpers/package-info.java
index 2bcdb37..88a4a2a 100644
--- a/src/main/java/org/apache/sling/servlethelpers/package-info.java
+++ b/src/main/java/org/apache/sling/servlethelpers/package-info.java
@@ -19,5 +19,5 @@
/**
* Mock implementation of selected Servlet-related Sling APIs.
*/
-@org.osgi.annotation.versioning.Version("1.9")
+@org.osgi.annotation.versioning.Version("1.10")
package org.apache.sling.servlethelpers;
diff --git a/src/test/java/org/apache/sling/servlethelpers/internalrequests/JakartaRequestInfoServlet.java b/src/test/java/org/apache/sling/servlethelpers/internalrequests/JakartaRequestInfoServlet.java
new file mode 100644
index 0000000..b933eec
--- /dev/null
+++ b/src/test/java/org/apache/sling/servlethelpers/internalrequests/JakartaRequestInfoServlet.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.servlethelpers.internalrequests;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import jakarta.servlet.ServletException;
+import org.apache.commons.io.IOUtils;
+import org.apache.sling.api.SlingJakartaHttpServletRequest;
+import org.apache.sling.api.SlingJakartaHttpServletResponse;
+import org.apache.sling.api.request.RequestPathInfo;
+import org.apache.sling.api.servlets.SlingJakartaAllMethodsServlet;
+
+class JakartaRequestInfoServlet extends SlingJakartaAllMethodsServlet {
+ private static final long serialVersionUID = 1L;
+ private static final String PREFIX = "TEST_";
+ private final String resolutionInfo;
+
+ JakartaRequestInfoServlet(SlingJakartaHttpServletRequest resolutionRequest) {
+ final StringBuilder sb = new StringBuilder();
+
+ // Verify that we get the usual Sling request
+ // attributes in the resolution call
+ final RequestPathInfo rpi = resolutionRequest.getRequestPathInfo();
+ sb.append("M_").append(resolutionRequest.getMethod());
+ sb.append(" PI_").append(resolutionRequest.getPathInfo());
+ sb.append(" RPI_EXT_").append(rpi.getExtension());
+ sb.append(" RPI_SEL_").append(rpi.getSelectorString());
+ sb.append(" RPI_P_").append(rpi.getResourcePath());
+ if (resolutionRequest.getResource() != null) {
+ sb.append(" RT_").append(resolutionRequest.getResource().getResourceType());
+ sb.append(" RST_").append(resolutionRequest.getResource().getResourceSuperType());
+ }
+ if (resolutionRequest.getResource().getResourceResolver() != null) {
+ sb.append(" RRA_")
+ .append(resolutionRequest
+ .getResource()
+ .getResourceResolver()
+ .getAttribute("testAttribute"));
+ }
+
+ resolutionInfo = sb.toString();
+ }
+
+ @Override
+ public void service(SlingJakartaHttpServletRequest request, SlingJakartaHttpServletResponse response)
+ throws IOException, ServletException {
+ if ("/EXCEPTION".equals(request.getResource().getPath())) {
+ throw new IOException("Failing as designed");
+ }
+ if ("/SERVLET-EXCEPTION".equals(request.getResource().getPath())) {
+ throw new ServletException("Failing as designed");
+ }
+ if ("STATUS".equals(request.getMethod())) {
+ response.setContentType("farenheit");
+ response.sendError(451);
+ return;
+ }
+
+ final SortedMap<String, String> sorted = new TreeMap<>();
+ request.getParameterMap().entrySet().stream().forEach(e -> {
+ sorted.put(e.getKey(), Arrays.asList(e.getValue()).toString());
+ });
+
+ response.getWriter().write(resolutionInfo);
+ response.getWriter().write(" CT_" + request.getContentType());
+ response.getWriter().write(" P_" + sorted);
+ final BufferedReader body = request.getReader();
+ if (body != null) {
+ response.getWriter().write(" B_");
+ IOUtils.copy(body, response.getWriter());
+ }
+ response.getWriter().flush();
+
+ response.setContentType(PREFIX + request.getContentType());
+ response.setContentType("CT_" + request.getContentType());
+ }
+}
diff --git a/src/test/java/org/apache/sling/servlethelpers/internalrequests/MockServletResolver.java b/src/test/java/org/apache/sling/servlethelpers/internalrequests/MockServletResolver.java
index 226e7d0..1c2e976 100644
--- a/src/test/java/org/apache/sling/servlethelpers/internalrequests/MockServletResolver.java
+++ b/src/test/java/org/apache/sling/servlethelpers/internalrequests/MockServletResolver.java
@@ -21,9 +21,12 @@ package org.apache.sling.servlethelpers.internalrequests;
import javax.servlet.Servlet;
import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingJakartaHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.servlets.ServletResolver;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
class MockServletResolver implements ServletResolver {
@@ -46,4 +49,19 @@ class MockServletResolver implements ServletResolver {
public Servlet resolveServlet(ResourceResolver resolver, String scriptName) {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public jakarta.servlet.@Nullable Servlet resolve(@NotNull SlingJakartaHttpServletRequest request) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public jakarta.servlet.@Nullable Servlet resolve(@NotNull Resource resource, @NotNull String scriptName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public jakarta.servlet.@Nullable Servlet resolve(@NotNull ResourceResolver resolver, @NotNull String scriptName) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/src/test/java/org/apache/sling/servlethelpers/internalrequests/MockSlingRequestProcessor.java b/src/test/java/org/apache/sling/servlethelpers/internalrequests/MockSlingRequestProcessor.java
index 3c22901..b9e956c 100644
--- a/src/test/java/org/apache/sling/servlethelpers/internalrequests/MockSlingRequestProcessor.java
+++ b/src/test/java/org/apache/sling/servlethelpers/internalrequests/MockSlingRequestProcessor.java
@@ -25,6 +25,8 @@ import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingJakartaHttpServletRequest;
+import org.apache.sling.api.SlingServletException;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.engine.SlingRequestProcessor;
@@ -42,4 +44,23 @@ class MockSlingRequestProcessor implements SlingRequestProcessor {
new RequestInfoServlet((SlingHttpServletRequest) request).service(request, response);
}
}
+
+ @Override
+ public void processRequest(
+ jakarta.servlet.http.HttpServletRequest httpRequest,
+ jakarta.servlet.http.HttpServletResponse response,
+ ResourceResolver resourceResolver)
+ throws IOException {
+ final SlingJakartaHttpServletRequest request = (SlingJakartaHttpServletRequest) httpRequest;
+ if (request.getResource() != null
+ && "/NOSERVLET".equals(request.getResource().getPath())) {
+ response.sendError(404);
+ } else {
+ try {
+ new JakartaRequestInfoServlet((SlingJakartaHttpServletRequest) request).service(request, response);
+ } catch (final jakarta.servlet.ServletException e) {
+ throw new SlingServletException(e);
+ }
+ }
+ }
}