getHeaderNames() {
+ return from.getHeaderNames();
+ }
+ };
+ }
+}
diff --git a/core/src/main/java/io/jenkins/servlet/package-info.java b/core/src/main/java/io/jenkins/servlet/package-info.java
new file mode 100644
index 000000000..ba36eee03
--- /dev/null
+++ b/core/src/main/java/io/jenkins/servlet/package-info.java
@@ -0,0 +1,8 @@
+/**
+ * Compatibility layer to convert to/from EE 8/9.
+ *
+ * All files in this package are kept separate from the {@code javax.servlet} package namespace to avoid potential
+ * trademark issues. Unfortunately, this results in a lot of static methods rather than object-oriented programming. The
+ * resulting loss of readability is unfortunate but necessary.
+ */
+package io.jenkins.servlet;
diff --git a/core/src/main/java/org/kohsuke/stapler/AcceptHeader.java b/core/src/main/java/org/kohsuke/stapler/AcceptHeader.java
index 9af006944..bfd90f21f 100644
--- a/core/src/main/java/org/kohsuke/stapler/AcceptHeader.java
+++ b/core/src/main/java/org/kohsuke/stapler/AcceptHeader.java
@@ -23,12 +23,12 @@ of this software and associated documentation files (the "Software"), to deal
package org.kohsuke.stapler;
import edu.umd.cs.findbugs.annotations.Nullable;
+import jakarta.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
diff --git a/core/src/main/java/org/kohsuke/stapler/Ancestor.java b/core/src/main/java/org/kohsuke/stapler/Ancestor.java
index 06ee8c438..4857c9708 100644
--- a/core/src/main/java/org/kohsuke/stapler/Ancestor.java
+++ b/core/src/main/java/org/kohsuke/stapler/Ancestor.java
@@ -23,7 +23,7 @@
package org.kohsuke.stapler;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
/**
* Information about ancestor of the "it" node.
diff --git a/core/src/main/java/org/kohsuke/stapler/AncestorImpl.java b/core/src/main/java/org/kohsuke/stapler/AncestorImpl.java
index ad6e070ae..03f153cc4 100644
--- a/core/src/main/java/org/kohsuke/stapler/AncestorImpl.java
+++ b/core/src/main/java/org/kohsuke/stapler/AncestorImpl.java
@@ -88,7 +88,7 @@ public String getRestOfUrl() {
@Override
public String getFullUrl() {
StringBuilder buf = new StringBuilder();
- StaplerRequest req = Stapler.getCurrentRequest();
+ StaplerRequest2 req = Stapler.getCurrentRequest2();
buf.append(req.getScheme());
buf.append("://");
buf.append(req.getServerName());
diff --git a/core/src/main/java/org/kohsuke/stapler/AncestorInPath.java b/core/src/main/java/org/kohsuke/stapler/AncestorInPath.java
index c899cc3e8..10c66060d 100644
--- a/core/src/main/java/org/kohsuke/stapler/AncestorInPath.java
+++ b/core/src/main/java/org/kohsuke/stapler/AncestorInPath.java
@@ -23,17 +23,17 @@
package org.kohsuke.stapler;
+import jakarta.servlet.ServletException;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import javax.servlet.ServletException;
import org.kohsuke.stapler.AncestorInPath.HandlerImpl;
/**
* Indicates that this parameter is injected by evaluating
- * {@link StaplerRequest#findAncestorObject(Class)} with the parameter type.
+ * {@link StaplerRequest2#findAncestorObject(Class)} with the parameter type.
*
* @author Kohsuke Kawaguchi
*/
@@ -44,7 +44,7 @@
public @interface AncestorInPath {
class HandlerImpl extends AnnotationHandler {
@Override
- public Object parse(StaplerRequest request, AncestorInPath a, Class type, String parameterName)
+ public Object parse(StaplerRequest2 request, AncestorInPath a, Class type, String parameterName)
throws ServletException {
return request.findAncestorObject(type);
}
diff --git a/core/src/main/java/org/kohsuke/stapler/AnnotationHandler.java b/core/src/main/java/org/kohsuke/stapler/AnnotationHandler.java
index 3ef661589..feb3abaa6 100644
--- a/core/src/main/java/org/kohsuke/stapler/AnnotationHandler.java
+++ b/core/src/main/java/org/kohsuke/stapler/AnnotationHandler.java
@@ -23,9 +23,10 @@
package org.kohsuke.stapler;
+import io.jenkins.servlet.ServletExceptionWrapper;
+import jakarta.servlet.ServletException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
-import javax.servlet.ServletException;
import org.apache.commons.beanutils.Converter;
/**
@@ -48,10 +49,54 @@ public abstract class AnnotationHandler {
* @param parameterName
* Name of the parameter.
*/
- public abstract Object parse(StaplerRequest request, T a, Class type, String parameterName) throws ServletException;
+ public /* abstract */ Object parse(StaplerRequest2 request, T a, Class type, String parameterName)
+ throws ServletException {
+ if (ReflectionUtils.isOverridden(
+ AnnotationHandler.class,
+ getClass(),
+ "parse",
+ StaplerRequest.class,
+ Annotation.class,
+ Class.class,
+ String.class)) {
+ try {
+ return parse(StaplerRequest.fromStaplerRequest2(request), a, type, parameterName);
+ } catch (javax.servlet.ServletException e) {
+ throw ServletExceptionWrapper.toJakartaServletException(e);
+ }
+ } else {
+ throw new AbstractMethodError("The class " + getClass().getName() + " must override at least one of the "
+ + AnnotationHandler.class.getSimpleName() + ".parse methods");
+ }
+ }
+
+ /**
+ * @deprecated use {@link #parse(StaplerRequest2, Annotation, Class, String)}
+ */
+ @Deprecated
+ public Object parse(StaplerRequest request, T a, Class type, String parameterName)
+ throws javax.servlet.ServletException {
+ if (ReflectionUtils.isOverridden(
+ AnnotationHandler.class,
+ getClass(),
+ "parse",
+ StaplerRequest2.class,
+ Annotation.class,
+ Class.class,
+ String.class)) {
+ try {
+ return parse(StaplerRequest.toStaplerRequest2(request), a, type, parameterName);
+ } catch (ServletException e) {
+ throw ServletExceptionWrapper.fromJakartaServletException(e);
+ }
+ } else {
+ throw new AbstractMethodError("The class " + getClass().getName() + " must override at least one of the "
+ + AnnotationHandler.class.getSimpleName() + ".parse methods");
+ }
+ }
/**
- * Helper method for {@link #parse(StaplerRequest, Annotation, Class, String)} to convert to the right type
+ * Helper method for {@link #parse(StaplerRequest2, Annotation, Class, String)} to convert to the right type
* from String.
*/
protected final Object convert(Class targetType, String value) {
@@ -63,7 +108,7 @@ protected final Object convert(Class targetType, String value) {
return converter.convert(targetType, value);
}
- static Object handle(StaplerRequest request, Annotation[] annotations, String parameterName, Class targetType)
+ static Object handle(StaplerRequest2 request, Annotation[] annotations, String parameterName, Class targetType)
throws ServletException {
for (Annotation a : annotations) {
Class extends Annotation> at = a.annotationType();
@@ -98,7 +143,7 @@ protected AnnotationHandler computeValue(Class> at) {
private static final AnnotationHandler NOT_HANDLER = new AnnotationHandler() {
@Override
- public Object parse(StaplerRequest request, Annotation a, Class type, String parameterName)
+ public Object parse(StaplerRequest2 request, Annotation a, Class type, String parameterName)
throws ServletException {
return null;
}
diff --git a/core/src/main/java/org/kohsuke/stapler/AttributeKey.java b/core/src/main/java/org/kohsuke/stapler/AttributeKey.java
index 427aaba55..95d282f90 100644
--- a/core/src/main/java/org/kohsuke/stapler/AttributeKey.java
+++ b/core/src/main/java/org/kohsuke/stapler/AttributeKey.java
@@ -1,9 +1,9 @@
package org.kohsuke.stapler;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpSession;
import java.util.UUID;
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
/**
* Type-safe attribute accessor.
@@ -39,15 +39,15 @@ public AttributeKey(String name) {
public abstract void remove(HttpServletRequest req);
public final T get() {
- return get(Stapler.getCurrentRequest());
+ return get(Stapler.getCurrentRequest2());
}
public final void set(T value) {
- set(Stapler.getCurrentRequest(), value);
+ set(Stapler.getCurrentRequest2(), value);
}
public final void remove() {
- remove(Stapler.getCurrentRequest());
+ remove(Stapler.getCurrentRequest2());
}
/**
diff --git a/core/src/main/java/org/kohsuke/stapler/BindInterceptor.java b/core/src/main/java/org/kohsuke/stapler/BindInterceptor.java
index 0db793f44..e17007130 100644
--- a/core/src/main/java/org/kohsuke/stapler/BindInterceptor.java
+++ b/core/src/main/java/org/kohsuke/stapler/BindInterceptor.java
@@ -7,7 +7,7 @@
* Intercepts (and receives callbacks) about the JSON → object binding process.
*
* @author Kohsuke Kawaguchi
- * @see StaplerRequest#setBindInterceptor(BindInterceptor)
+ * @see StaplerRequest2#setBindInterceptor(BindInterceptor)
* @see WebApp#bindInterceptors
*/
public class BindInterceptor {
diff --git a/core/src/main/java/org/kohsuke/stapler/ClassDescriptor.java b/core/src/main/java/org/kohsuke/stapler/ClassDescriptor.java
index 6351184b1..cd6a4e3e4 100644
--- a/core/src/main/java/org/kohsuke/stapler/ClassDescriptor.java
+++ b/core/src/main/java/org/kohsuke/stapler/ClassDescriptor.java
@@ -155,8 +155,8 @@ public int compare(Method m1, Method m2) {
} else if (!m1d && m2d) {
return -1;
} else {
- // Sort by string representation, so for example doFoo() is preferred to doFoo(StaplerRequest,
- // StaplerResponse).
+ // Sort by string representation, so for example doFoo() is preferred to doFoo(StaplerRequest2,
+ // StaplerResponse2).
return m1.toString().compareTo(m2.toString());
}
}
diff --git a/core/src/main/java/org/kohsuke/stapler/CompatibleFilter.java b/core/src/main/java/org/kohsuke/stapler/CompatibleFilter.java
new file mode 100644
index 000000000..0290a7f19
--- /dev/null
+++ b/core/src/main/java/org/kohsuke/stapler/CompatibleFilter.java
@@ -0,0 +1,59 @@
+package org.kohsuke.stapler;
+
+import io.jenkins.servlet.FilterChainWrapper;
+import io.jenkins.servlet.FilterConfigWrapper;
+import io.jenkins.servlet.ServletExceptionWrapper;
+import io.jenkins.servlet.ServletRequestWrapper;
+import io.jenkins.servlet.ServletResponseWrapper;
+import io.jenkins.servlet.http.HttpServletRequestWrapper;
+import io.jenkins.servlet.http.HttpServletResponseWrapper;
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import java.io.IOException;
+
+public interface CompatibleFilter extends Filter {
+ /**
+ * @deprecated use {@link #init(FilterConfig)}
+ */
+ @Deprecated
+ default void init(javax.servlet.FilterConfig filterConfig) throws javax.servlet.ServletException {
+ try {
+ init(FilterConfigWrapper.toJakartaFilterConfig(filterConfig));
+ } catch (ServletException e) {
+ throw ServletExceptionWrapper.fromJakartaServletException(e);
+ }
+ }
+
+ /**
+ * @deprecated use {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}
+ */
+ @Deprecated
+ default void doFilter(
+ javax.servlet.ServletRequest request,
+ javax.servlet.ServletResponse response,
+ javax.servlet.FilterChain chain)
+ throws IOException, javax.servlet.ServletException {
+ try {
+ if (request instanceof javax.servlet.http.HttpServletRequest
+ && response instanceof javax.servlet.http.HttpServletResponse) {
+ javax.servlet.http.HttpServletRequest httpRequest = (javax.servlet.http.HttpServletRequest) request;
+ javax.servlet.http.HttpServletResponse httpResponse = (javax.servlet.http.HttpServletResponse) response;
+ doFilter(
+ HttpServletRequestWrapper.toJakartaHttpServletRequest(httpRequest),
+ HttpServletResponseWrapper.toJakartaHttpServletResponse(httpResponse),
+ FilterChainWrapper.toJakartaFilterChain(chain));
+ } else {
+ doFilter(
+ ServletRequestWrapper.toJakartaServletRequest(request),
+ ServletResponseWrapper.toJakartaServletResponse(response),
+ FilterChainWrapper.toJakartaFilterChain(chain));
+ }
+ } catch (ServletException e) {
+ throw ServletExceptionWrapper.fromJakartaServletException(e);
+ }
+ }
+}
diff --git a/core/src/main/java/org/kohsuke/stapler/CrumbIssuer.java b/core/src/main/java/org/kohsuke/stapler/CrumbIssuer.java
index 5d5737955..dc2b8b57a 100644
--- a/core/src/main/java/org/kohsuke/stapler/CrumbIssuer.java
+++ b/core/src/main/java/org/kohsuke/stapler/CrumbIssuer.java
@@ -1,7 +1,7 @@
package org.kohsuke.stapler;
+import jakarta.servlet.http.HttpSession;
import java.util.UUID;
-import javax.servlet.http.HttpSession;
/**
* Generates a nonce value that allows us to protect against cross-site request forgery (CSRF) attacks.
@@ -17,10 +17,30 @@ public abstract class CrumbIssuer {
/**
* Issues a crumb for the given request.
*/
- public abstract String issueCrumb(StaplerRequest request);
+ public /* abstract */ String issueCrumb(StaplerRequest2 request) {
+ return ReflectionUtils.ifOverridden(
+ () -> issueCrumb(StaplerRequest.fromStaplerRequest2(request)),
+ CrumbIssuer.class,
+ getClass(),
+ "issueCrumb",
+ StaplerRequest.class);
+ }
+
+ /**
+ * @deprecated use {@link #issueCrumb(StaplerRequest2)}
+ */
+ @Deprecated
+ public String issueCrumb(StaplerRequest request) {
+ return ReflectionUtils.ifOverridden(
+ () -> issueCrumb(StaplerRequest.toStaplerRequest2(request)),
+ CrumbIssuer.class,
+ getClass(),
+ "issueCrumb",
+ StaplerRequest2.class);
+ }
public final String issueCrumb() {
- return issueCrumb(Stapler.getCurrentRequest());
+ return issueCrumb(Stapler.getCurrentRequest2());
}
/**
@@ -41,18 +61,26 @@ public HttpResponse doCrumb() {
* @throws SecurityException
* If the crumb doesn't match and the request processing should abort.
*/
- public void validateCrumb(StaplerRequest request, String submittedCrumb) {
+ public void validateCrumb(StaplerRequest2 request, String submittedCrumb) {
if (!issueCrumb(request).equals(submittedCrumb)) {
throw new SecurityException("Request failed to pass the crumb test (try clearing your cookies)");
}
}
+ /**
+ * @deprecated use {@link #validateCrumb(StaplerRequest2, String)}
+ */
+ @Deprecated
+ public void validateCrumb(StaplerRequest request, String submittedCrumb) {
+ validateCrumb(StaplerRequest.toStaplerRequest2(request), submittedCrumb);
+ }
+
/**
* Default crumb issuer.
*/
public static final CrumbIssuer DEFAULT = new CrumbIssuer() {
@Override
- public String issueCrumb(StaplerRequest request) {
+ public String issueCrumb(StaplerRequest2 request) {
HttpSession s = request.getSession();
String v = (String) s.getAttribute(ATTRIBUTE_NAME);
if (v != null) {
diff --git a/core/src/main/java/org/kohsuke/stapler/DataBoundConstructor.java b/core/src/main/java/org/kohsuke/stapler/DataBoundConstructor.java
index 749d20aaf..7406967a7 100644
--- a/core/src/main/java/org/kohsuke/stapler/DataBoundConstructor.java
+++ b/core/src/main/java/org/kohsuke/stapler/DataBoundConstructor.java
@@ -33,13 +33,13 @@
/**
* Designates the constructor to be created
* from methods like
- * {@link StaplerRequest#bindJSON(Class, JSONObject)} and
- * {@link StaplerRequest#bindParameters(Class, String)}.
+ * {@link StaplerRequest2#bindJSON(Class, JSONObject)} and
+ * {@link StaplerRequest2#bindParameters(Class, String)}.
*
*
* Stapler will invoke the designated constructor by using arguments from the corresponding
- * {@link JSONObject} (in case of {@link StaplerRequest#bindJSON(Class, JSONObject)}) or request parameters
- * (in case of {@link StaplerRequest#bindParameters(Class, String)}).
+ * {@link JSONObject} (in case of {@link StaplerRequest2#bindJSON(Class, JSONObject)}) or request parameters
+ * (in case of {@link StaplerRequest2#bindParameters(Class, String)}).
*
*
* The matching is done by using the constructor parameter name. Since this information is not available
diff --git a/core/src/main/java/org/kohsuke/stapler/DataBoundResolvable.java b/core/src/main/java/org/kohsuke/stapler/DataBoundResolvable.java
index b0723709e..a86821644 100644
--- a/core/src/main/java/org/kohsuke/stapler/DataBoundResolvable.java
+++ b/core/src/main/java/org/kohsuke/stapler/DataBoundResolvable.java
@@ -4,12 +4,12 @@
/**
* For data-bound class (that has a constructor marked with {@link DataBoundConstructor}, the
- * {@link #bindResolve(StaplerRequest, JSONObject)} allows an instance to replace the object
+ * {@link #bindResolve(StaplerRequest2, JSONObject)} allows an instance to replace the object
* bound from submitted JSON object.
*
*
* This method is automatically invoked by Stapler during databinding method like
- * {@link StaplerRequest#bindJSON(Class, JSONObject)}.
+ * {@link StaplerRequest2#bindJSON(Class, JSONObject)}.
*
*
* This method definition is inspired by Java serialization's {@code readResolve()} method.
@@ -31,5 +31,5 @@ public interface DataBoundResolvable {
* Can be any value, including null. Typically, this method would have to return an
* instance of a type compatible to the caller's expectation.
*/
- Object bindResolve(StaplerRequest request, JSONObject src);
+ Object bindResolve(StaplerRequest2 request, JSONObject src);
}
diff --git a/core/src/main/java/org/kohsuke/stapler/DataBoundSetter.java b/core/src/main/java/org/kohsuke/stapler/DataBoundSetter.java
index 7ed4c5930..7d0d2de03 100644
--- a/core/src/main/java/org/kohsuke/stapler/DataBoundSetter.java
+++ b/core/src/main/java/org/kohsuke/stapler/DataBoundSetter.java
@@ -11,8 +11,8 @@
/**
* Designates a setter method or a field used to databind JSON values into objects in methods like
- * {@link StaplerRequest#bindJSON(Class, JSONObject)} and
- * {@link StaplerRequest#bindParameters(Class, String)}.
+ * {@link StaplerRequest2#bindJSON(Class, JSONObject)} and
+ * {@link StaplerRequest2#bindParameters(Class, String)}.
*
*
* Stapler will first invoke {@link DataBoundConstructor}-annotated constructor, and if there's any
diff --git a/core/src/main/java/org/kohsuke/stapler/DiagnosticThreadNameFilter.java b/core/src/main/java/org/kohsuke/stapler/DiagnosticThreadNameFilter.java
index 7cef9f850..1b72836b1 100644
--- a/core/src/main/java/org/kohsuke/stapler/DiagnosticThreadNameFilter.java
+++ b/core/src/main/java/org/kohsuke/stapler/DiagnosticThreadNameFilter.java
@@ -1,20 +1,20 @@
package org.kohsuke.stapler;
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
/**
* {@link Filter} that sets the thread name to reflect the current request being processed.
*
* @author Kohsuke Kawaguchi
*/
-public class DiagnosticThreadNameFilter implements Filter {
+public class DiagnosticThreadNameFilter implements CompatibleFilter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
diff --git a/core/src/main/java/org/kohsuke/stapler/DirectoryishDispatcher.java b/core/src/main/java/org/kohsuke/stapler/DirectoryishDispatcher.java
index 9970c7088..3e7cd5404 100644
--- a/core/src/main/java/org/kohsuke/stapler/DirectoryishDispatcher.java
+++ b/core/src/main/java/org/kohsuke/stapler/DirectoryishDispatcher.java
@@ -1,10 +1,10 @@
package org.kohsuke.stapler;
+import jakarta.servlet.ServletException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.logging.Level;
import java.util.logging.Logger;
-import javax.servlet.ServletException;
/**
* {@link Dispatcher} that tells browsers to append '/' to the request path and try again.
diff --git a/core/src/main/java/org/kohsuke/stapler/DispatchValidator.java b/core/src/main/java/org/kohsuke/stapler/DispatchValidator.java
index e21a84060..e5237cf76 100644
--- a/core/src/main/java/org/kohsuke/stapler/DispatchValidator.java
+++ b/core/src/main/java/org/kohsuke/stapler/DispatchValidator.java
@@ -48,7 +48,7 @@ public interface DispatchValidator {
* @return true if the request should be dispatched, false if not, or null if unknown or neutral
*/
@CheckForNull
- Boolean isDispatchAllowed(@NonNull StaplerRequest req, @NonNull StaplerResponse rsp);
+ Boolean isDispatchAllowed(@NonNull StaplerRequest2 req, @NonNull StaplerResponse2 rsp);
/**
* Checks if the given request and response should be allowed to dispatch a view on an optionally present node
@@ -61,24 +61,24 @@ public interface DispatchValidator {
* @return true if the view should be allowed to dispatch, false if it should not, or null if unknown
*/
default @CheckForNull Boolean isDispatchAllowed(
- @NonNull StaplerRequest req,
- @NonNull StaplerResponse rsp,
+ @NonNull StaplerRequest2 req,
+ @NonNull StaplerResponse2 rsp,
@NonNull String viewName,
@CheckForNull Object node) {
return isDispatchAllowed(req, rsp);
}
/**
- * Allows the given request to be dispatched. Further calls to {@link #isDispatchAllowed(StaplerRequest, StaplerResponse)}
+ * Allows the given request to be dispatched. Further calls to {@link #isDispatchAllowed(StaplerRequest2, StaplerResponse2)}
* should return true for the same request.
*/
- void allowDispatch(@NonNull StaplerRequest req, @NonNull StaplerResponse rsp);
+ void allowDispatch(@NonNull StaplerRequest2 req, @NonNull StaplerResponse2 rsp);
/**
* Throws a {@link CancelRequestHandlingException} if the given request is not
- * {@linkplain #isDispatchAllowed(StaplerRequest, StaplerResponse) allowed}.
+ * {@linkplain #isDispatchAllowed(StaplerRequest2, StaplerResponse2) allowed}.
*/
- default void requireDispatchAllowed(@NonNull StaplerRequest req, @NonNull StaplerResponse rsp)
+ default void requireDispatchAllowed(@NonNull StaplerRequest2 req, @NonNull StaplerResponse2 rsp)
throws CancelRequestHandlingException {
Boolean allowed = isDispatchAllowed(req, rsp);
if (allowed == null || !allowed) {
@@ -91,12 +91,12 @@ default void requireDispatchAllowed(@NonNull StaplerRequest req, @NonNull Staple
*/
DispatchValidator DEFAULT = new DispatchValidator() {
@Override
- public Boolean isDispatchAllowed(@NonNull StaplerRequest req, @NonNull StaplerResponse rsp) {
+ public Boolean isDispatchAllowed(@NonNull StaplerRequest2 req, @NonNull StaplerResponse2 rsp) {
return true;
}
@Override
- public void allowDispatch(@NonNull StaplerRequest req, @NonNull StaplerResponse rsp) {
+ public void allowDispatch(@NonNull StaplerRequest2 req, @NonNull StaplerResponse2 rsp) {
// no-op
}
};
diff --git a/core/src/main/java/org/kohsuke/stapler/Dispatcher.java b/core/src/main/java/org/kohsuke/stapler/Dispatcher.java
index 17e52b5ee..6b5fd1f63 100644
--- a/core/src/main/java/org/kohsuke/stapler/Dispatcher.java
+++ b/core/src/main/java/org/kohsuke/stapler/Dispatcher.java
@@ -24,6 +24,7 @@
package org.kohsuke.stapler;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import jakarta.servlet.ServletException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
@@ -31,7 +32,6 @@
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
-import javax.servlet.ServletException;
/**
* Controls the dispatching of incoming HTTP requests.
@@ -63,7 +63,7 @@ public static boolean traceable() {
return TRACE || TRACE_PER_REQUEST || LOGGER.isLoggable(Level.FINE);
}
- public static void traceEval(StaplerRequest req, StaplerResponse rsp, Object node) {
+ public static void traceEval(StaplerRequest2 req, StaplerResponse2 rsp, Object node) {
trace(
req,
rsp,
@@ -75,14 +75,14 @@ public static void traceEval(StaplerRequest req, StaplerResponse rsp, Object nod
}
public static void anonymizedTraceEval(
- StaplerRequest req, StaplerResponse rsp, Object node, String format, String... args) {
+ StaplerRequest2 req, StaplerResponse2 rsp, Object node, String format, String... args) {
List arg = new ArrayList<>();
arg.add(node == null ? "(null)" : node.getClass().getName());
arg.addAll(Arrays.asList(args));
EvaluationTrace.ApplicationTracer.trace(req, String.format(format, arg.toArray()));
}
- public static void traceEval(StaplerRequest req, StaplerResponse rsp, Object node, String prefix, String suffix) {
+ public static void traceEval(StaplerRequest2 req, StaplerResponse2 rsp, Object node, String prefix, String suffix) {
trace(
req,
rsp,
@@ -91,7 +91,7 @@ public static void traceEval(StaplerRequest req, StaplerResponse rsp, Object nod
prefix, node, suffix, ((RequestImpl) req).tokens.assembleOriginalRestOfPath()));
}
- public static void traceEval(StaplerRequest req, StaplerResponse rsp, Object node, String expression) {
+ public static void traceEval(StaplerRequest2 req, StaplerResponse2 rsp, Object node, String expression) {
trace(
req,
rsp,
@@ -100,11 +100,11 @@ public static void traceEval(StaplerRequest req, StaplerResponse rsp, Object nod
node, expression, ((RequestImpl) req).tokens.assembleOriginalRestOfPath()));
}
- public static void trace(StaplerRequest req, StaplerResponse rsp, String msg, Object... args) {
+ public static void trace(StaplerRequest2 req, StaplerResponse2 rsp, String msg, Object... args) {
trace(req, rsp, String.format(msg, args));
}
- public static void trace(StaplerRequest req, StaplerResponse rsp, String msg) {
+ public static void trace(StaplerRequest2 req, StaplerResponse2 rsp, String msg) {
if (isTraceEnabled(req)) {
EvaluationTrace.get(req).trace(rsp, msg);
}
@@ -119,7 +119,7 @@ public static void trace(StaplerRequest req, StaplerResponse rsp, String msg) {
* can be enabled per-request by setting "stapler.trace.per-request=true"
* and sending an "X-Stapler-Trace" header set to "true" with the request.
*/
- public static boolean isTraceEnabled(StaplerRequest req) {
+ public static boolean isTraceEnabled(StaplerRequest2 req) {
if (TRACE) {
return true;
}
diff --git a/core/src/main/java/org/kohsuke/stapler/EvaluationTrace.java b/core/src/main/java/org/kohsuke/stapler/EvaluationTrace.java
index 0d012985a..7a6e6ff5f 100644
--- a/core/src/main/java/org/kohsuke/stapler/EvaluationTrace.java
+++ b/core/src/main/java/org/kohsuke/stapler/EvaluationTrace.java
@@ -42,7 +42,7 @@ public class EvaluationTrace {
private static final Logger LOGGER = Logger.getLogger(EvaluationTrace.class.getName());
- public void trace(StaplerResponse rsp, String msg) {
+ public void trace(StaplerResponse2 rsp, String msg) {
traces.add(msg);
// Firefox Live HTTP header plugin cannot nicely render multiple headers
// with the same name, so give each one unique name.
@@ -57,7 +57,7 @@ public void printHtml(PrintWriter w) {
}
}
- public static EvaluationTrace get(StaplerRequest req) {
+ public static EvaluationTrace get(StaplerRequest2 req) {
EvaluationTrace et = (EvaluationTrace) req.getAttribute(KEY);
if (et == null) {
req.setAttribute(KEY, et = new EvaluationTrace());
@@ -71,9 +71,9 @@ public static EvaluationTrace get(StaplerRequest req) {
private static final String KEY = EvaluationTrace.class.getName();
public abstract static class ApplicationTracer {
- protected abstract void record(StaplerRequest req, String message);
+ protected abstract void record(StaplerRequest2 req, String message);
- public static void trace(StaplerRequest req, String message) {
+ public static void trace(StaplerRequest2 req, String message) {
List tracers = getTracers();
for (ApplicationTracer tracer : tracers) {
tracer.record(req, message);
diff --git a/core/src/main/java/org/kohsuke/stapler/Facet.java b/core/src/main/java/org/kohsuke/stapler/Facet.java
index d77416ac8..277a5940b 100644
--- a/core/src/main/java/org/kohsuke/stapler/Facet.java
+++ b/core/src/main/java/org/kohsuke/stapler/Facet.java
@@ -26,6 +26,8 @@
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import jakarta.servlet.RequestDispatcher;
+import jakarta.servlet.ServletException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
@@ -33,8 +35,6 @@
import java.util.ServiceLoader;
import java.util.Set;
import java.util.logging.Logger;
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletException;
import org.kohsuke.MetaInfServices;
import org.kohsuke.stapler.event.FilteredDispatchTriggerListener;
import org.kohsuke.stapler.lang.Klass;
diff --git a/core/src/main/java/org/kohsuke/stapler/ForwardToView.java b/core/src/main/java/org/kohsuke/stapler/ForwardToView.java
index 2357e8ab5..641e39b9c 100644
--- a/core/src/main/java/org/kohsuke/stapler/ForwardToView.java
+++ b/core/src/main/java/org/kohsuke/stapler/ForwardToView.java
@@ -24,12 +24,12 @@
package org.kohsuke.stapler;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import jakarta.servlet.RequestDispatcher;
+import jakarta.servlet.ServletException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletException;
/**
* {@link HttpResponse} that forwards to a {@link RequestDispatcher}, such as a view.
@@ -44,13 +44,13 @@ public class ForwardToView extends RuntimeException implements HttpResponse {
private final Map attributes = new HashMap<>();
private interface DispatcherFactory {
- RequestDispatcher get(StaplerRequest req) throws IOException;
+ RequestDispatcher get(StaplerRequest2 req) throws IOException;
}
public ForwardToView(final RequestDispatcher dispatcher) {
this.factory = new DispatcherFactory() {
@Override
- public RequestDispatcher get(StaplerRequest req) {
+ public RequestDispatcher get(StaplerRequest2 req) {
return dispatcher;
}
};
@@ -59,7 +59,7 @@ public RequestDispatcher get(StaplerRequest req) {
public ForwardToView(final Object it, final String view) {
this.factory = new DispatcherFactory() {
@Override
- public RequestDispatcher get(StaplerRequest req) throws IOException {
+ public RequestDispatcher get(StaplerRequest2 req) throws IOException {
return req.getView(it, view);
}
};
@@ -68,7 +68,7 @@ public RequestDispatcher get(StaplerRequest req) throws IOException {
public ForwardToView(final Class c, final String view) {
this.factory = new DispatcherFactory() {
@Override
- public RequestDispatcher get(StaplerRequest req) throws IOException {
+ public RequestDispatcher get(StaplerRequest2 req) throws IOException {
return req.getView(c, view);
}
};
@@ -98,7 +98,7 @@ public ForwardToView optional() {
@SuppressFBWarnings(
value = "REQUESTDISPATCHER_FILE_DISCLOSURE",
justification = "Forwarded to a view to handle correctly.")
- public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node)
+ public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node)
throws IOException, ServletException {
for (Entry e : attributes.entrySet()) {
req.setAttribute(e.getKey(), e.getValue());
diff --git a/core/src/main/java/org/kohsuke/stapler/ForwardingFunction.java b/core/src/main/java/org/kohsuke/stapler/ForwardingFunction.java
index 66e08b5ef..de80df956 100644
--- a/core/src/main/java/org/kohsuke/stapler/ForwardingFunction.java
+++ b/core/src/main/java/org/kohsuke/stapler/ForwardingFunction.java
@@ -1,9 +1,9 @@
package org.kohsuke.stapler;
+import jakarta.servlet.ServletException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
-import javax.servlet.ServletException;
/**
* {@link Function} that forwards calls to another. Usually used
@@ -85,7 +85,7 @@ public String[] getParameterNames() {
}
@Override
- public Object invoke(StaplerRequest req, StaplerResponse rsp, Object o, Object... args)
+ public Object invoke(StaplerRequest2 req, StaplerResponse2 rsp, Object o, Object... args)
throws IllegalAccessException, InvocationTargetException, ServletException {
return next.invoke(req, rsp, o, args);
}
diff --git a/core/src/main/java/org/kohsuke/stapler/Function.java b/core/src/main/java/org/kohsuke/stapler/Function.java
index 79c371097..e615a8d64 100644
--- a/core/src/main/java/org/kohsuke/stapler/Function.java
+++ b/core/src/main/java/org/kohsuke/stapler/Function.java
@@ -23,6 +23,10 @@
package org.kohsuke.stapler;
+import io.jenkins.servlet.ServletExceptionWrapper;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.annotation.Annotation;
@@ -39,9 +43,6 @@
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import org.kohsuke.stapler.interceptor.Interceptor;
import org.kohsuke.stapler.interceptor.InterceptorAnnotation;
@@ -125,7 +126,7 @@ public Function contextualize(Object usage) {
}
/**
- * Calls {@link #bindAndInvoke(Object, StaplerRequest, StaplerResponse, Object...)} and then
+ * Calls {@link #bindAndInvoke(Object, StaplerRequest2, StaplerResponse2, Object...)} and then
* optionally serve the response object.
*
* @return
@@ -171,7 +172,7 @@ static boolean renderResponse(RequestImpl req, ResponseImpl rsp, Object node, Ob
* then figure out the rest of the arguments by looking at parameter annotations,
* then finally call {@link #invoke}.
*/
- Object bindAndInvoke(Object o, StaplerRequest req, StaplerResponse rsp, Object... headArgs)
+ Object bindAndInvoke(Object o, StaplerRequest2 req, StaplerResponse2 rsp, Object... headArgs)
throws IllegalAccessException, InvocationTargetException, ServletException {
Class[] types = getParameterTypes();
Annotation[][] annotations = getParameterAnnotations();
@@ -186,13 +187,18 @@ Object bindAndInvoke(Object o, StaplerRequest req, StaplerResponse rsp, Object..
// find the rest of the arguments. either known types, or with annotations
for (int i = headArgs.length; i < types.length; i++) {
Class t = types[i];
- if (t == StaplerRequest.class || t == HttpServletRequest.class) {
+ if (t == StaplerRequest2.class || t == HttpServletRequest.class) {
arguments[i] = req;
continue;
- }
- if (t == StaplerResponse.class || t == HttpServletResponse.class) {
+ } else if (t == StaplerRequest.class || t == javax.servlet.http.HttpServletRequest.class) {
+ arguments[i] = StaplerRequest.fromStaplerRequest2(req);
+ continue;
+ } else if (t == StaplerResponse2.class || t == HttpServletResponse.class) {
arguments[i] = rsp;
continue;
+ } else if (t == StaplerResponse.class || t == javax.servlet.http.HttpServletResponse.class) {
+ arguments[i] = StaplerResponse.fromStaplerResponse2(rsp);
+ continue;
}
// if the databinding method is provided, call that
@@ -254,7 +260,7 @@ public Annotation[][] getParameterAnnotations() {
}
@Override
- public Object invoke(StaplerRequest req, StaplerResponse rsp, Object o, Object... args)
+ public Object invoke(StaplerRequest2 req, StaplerResponse2 rsp, Object o, Object... args)
throws IllegalAccessException, InvocationTargetException {
return m.invoke(null, args);
}
@@ -276,8 +282,52 @@ public static Object returnNull() {
/**
* Invokes the method.
*/
- public abstract Object invoke(StaplerRequest req, StaplerResponse rsp, Object o, Object... args)
- throws IllegalAccessException, InvocationTargetException, ServletException;
+ public /* abstract */ Object invoke(StaplerRequest2 req, StaplerResponse2 rsp, Object o, Object... args)
+ throws IllegalAccessException, InvocationTargetException, ServletException {
+ if (ReflectionUtils.isOverridden(
+ Function.class,
+ getClass(),
+ "invoke",
+ StaplerRequest.class,
+ StaplerResponse.class,
+ Object.class,
+ Object[].class)) {
+ try {
+ return invoke(
+ StaplerRequest.fromStaplerRequest2(req), StaplerResponse.fromStaplerResponse2(rsp), o, args);
+ } catch (javax.servlet.ServletException e) {
+ throw ServletExceptionWrapper.toJakartaServletException(e);
+ }
+ } else {
+ throw new AbstractMethodError("The class " + getClass().getName() + " must override at least one of the "
+ + Function.class.getSimpleName() + ".invoke methods");
+ }
+ }
+
+ /**
+ * @deprecated use {@link #invoke(StaplerRequest2, StaplerResponse2, Object, Object...)}
+ */
+ @Deprecated
+ public Object invoke(StaplerRequest req, StaplerResponse rsp, Object o, Object... args)
+ throws IllegalAccessException, InvocationTargetException, javax.servlet.ServletException {
+ if (ReflectionUtils.isOverridden(
+ Function.class,
+ getClass(),
+ "invoke",
+ StaplerRequest2.class,
+ StaplerResponse2.class,
+ Object.class,
+ Object[].class)) {
+ try {
+ return invoke(StaplerRequest.toStaplerRequest2(req), StaplerResponse.toStaplerResponse2(rsp), o, args);
+ } catch (ServletException e) {
+ throw ServletExceptionWrapper.fromJakartaServletException(e);
+ }
+ } else {
+ throw new AbstractMethodError("The class " + getClass().getName() + " must override at least one of the "
+ + Function.class.getSimpleName() + ".invoke methods");
+ }
+ }
final Function wrapByInterceptors(AnnotatedElement m) {
try {
@@ -420,7 +470,7 @@ protected MethodHandle handle() {
}
@Override
- public Object invoke(StaplerRequest req, StaplerResponse rsp, Object o, Object... args)
+ public Object invoke(StaplerRequest2 req, StaplerResponse2 rsp, Object o, Object... args)
throws IllegalAccessException, InvocationTargetException {
Object[] arguments;
if (Modifier.isStatic(m.getModifiers())) {
diff --git a/core/src/main/java/org/kohsuke/stapler/Header.java b/core/src/main/java/org/kohsuke/stapler/Header.java
index cd4e53072..74faea7a0 100644
--- a/core/src/main/java/org/kohsuke/stapler/Header.java
+++ b/core/src/main/java/org/kohsuke/stapler/Header.java
@@ -23,12 +23,12 @@
package org.kohsuke.stapler;
+import jakarta.servlet.ServletException;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import javax.servlet.ServletException;
import org.kohsuke.stapler.Header.HandlerImpl;
/**
@@ -53,7 +53,7 @@
class HandlerImpl extends AnnotationHandler {
@Override
- public Object parse(StaplerRequest request, Header a, Class type, String parameterName)
+ public Object parse(StaplerRequest2 request, Header a, Class type, String parameterName)
throws ServletException {
String name = a.value();
if (name.isEmpty()) {
diff --git a/core/src/main/java/org/kohsuke/stapler/HttpDeletable.java b/core/src/main/java/org/kohsuke/stapler/HttpDeletable.java
index 3689859f9..f5278c5b1 100644
--- a/core/src/main/java/org/kohsuke/stapler/HttpDeletable.java
+++ b/core/src/main/java/org/kohsuke/stapler/HttpDeletable.java
@@ -23,9 +23,10 @@
package org.kohsuke.stapler;
+import io.jenkins.servlet.ServletExceptionWrapper;
+import jakarta.servlet.ServletException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
-import javax.servlet.ServletException;
/**
* Marks the object that can handle HTTP DELETE.
@@ -36,7 +37,37 @@ public interface HttpDeletable {
/**
* Called when HTTP DELETE method is invoked.
*/
- void delete(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException;
+ default void delete(StaplerRequest2 req, StaplerResponse2 rsp) throws IOException, ServletException {
+ if (ReflectionUtils.isOverridden(
+ HttpDeletable.class, getClass(), "delete", StaplerRequest.class, StaplerResponse.class)) {
+ try {
+ delete(StaplerRequest.fromStaplerRequest2(req), StaplerResponse.fromStaplerResponse2(rsp));
+ } catch (javax.servlet.ServletException e) {
+ throw ServletExceptionWrapper.toJakartaServletException(e);
+ }
+ } else {
+ throw new AbstractMethodError("The class " + getClass().getName() + " must override at least one of the "
+ + HttpDeletable.class.getSimpleName() + ".delete methods");
+ }
+ }
+
+ /**
+ * @deprecated use {@link #delete(StaplerRequest2, StaplerResponse2)}
+ */
+ @Deprecated
+ default void delete(StaplerRequest req, StaplerResponse rsp) throws IOException, javax.servlet.ServletException {
+ if (ReflectionUtils.isOverridden(
+ HttpDeletable.class, getClass(), "delete", StaplerRequest2.class, StaplerResponse2.class)) {
+ try {
+ delete(StaplerRequest.toStaplerRequest2(req), StaplerResponse.toStaplerResponse2(rsp));
+ } catch (ServletException e) {
+ throw ServletExceptionWrapper.fromJakartaServletException(e);
+ }
+ } else {
+ throw new AbstractMethodError("The class " + getClass().getName() + " must override at least one of the "
+ + HttpDeletable.class.getSimpleName() + ".delete methods");
+ }
+ }
/**
* {@link Dispatcher} that processes {@link HttpDeletable}
diff --git a/core/src/main/java/org/kohsuke/stapler/HttpRedirect.java b/core/src/main/java/org/kohsuke/stapler/HttpRedirect.java
index 39e123eba..357034c8b 100644
--- a/core/src/main/java/org/kohsuke/stapler/HttpRedirect.java
+++ b/core/src/main/java/org/kohsuke/stapler/HttpRedirect.java
@@ -24,9 +24,9 @@
package org.kohsuke.stapler;
import edu.umd.cs.findbugs.annotations.NonNull;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletResponse;
/**
* {@link HttpResponse} that dose HTTP 302 redirect.
@@ -51,7 +51,7 @@ public HttpRedirect(int statusCode, @NonNull String url) {
}
@Override
- public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node)
+ public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node)
throws IOException, ServletException {
rsp.sendRedirect(statusCode, url);
}
diff --git a/core/src/main/java/org/kohsuke/stapler/HttpResponse.java b/core/src/main/java/org/kohsuke/stapler/HttpResponse.java
index 44306b867..59d069e51 100644
--- a/core/src/main/java/org/kohsuke/stapler/HttpResponse.java
+++ b/core/src/main/java/org/kohsuke/stapler/HttpResponse.java
@@ -23,8 +23,9 @@
package org.kohsuke.stapler;
+import io.jenkins.servlet.ServletExceptionWrapper;
+import jakarta.servlet.ServletException;
import java.io.IOException;
-import javax.servlet.ServletException;
/**
* Object that represents the HTTP response, which is defined as a capability to produce the response.
@@ -43,5 +44,53 @@ public interface HttpResponse {
* @param node
* The object whose "doXyz" method created this object.
*/
- void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException;
+ default void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node)
+ throws IOException, ServletException {
+ if (ReflectionUtils.isOverridden(
+ HttpResponse.class,
+ getClass(),
+ "generateResponse",
+ StaplerRequest.class,
+ StaplerResponse.class,
+ Object.class)) {
+ try {
+ generateResponse(
+ req != null ? StaplerRequest.fromStaplerRequest2(req) : null,
+ rsp != null ? StaplerResponse.fromStaplerResponse2(rsp) : null,
+ node);
+ } catch (javax.servlet.ServletException e) {
+ throw ServletExceptionWrapper.toJakartaServletException(e);
+ }
+ } else {
+ throw new AbstractMethodError("The class " + getClass().getName() + " must override at least one of the "
+ + HttpResponse.class.getSimpleName() + ".generateResponse methods");
+ }
+ }
+
+ /**
+ * @deprecated use {@link #generateResponse(StaplerRequest2, StaplerResponse2, Object)}
+ */
+ @Deprecated
+ default void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node)
+ throws IOException, javax.servlet.ServletException {
+ if (ReflectionUtils.isOverridden(
+ HttpResponse.class,
+ getClass(),
+ "generateResponse",
+ StaplerRequest2.class,
+ StaplerResponse2.class,
+ Object.class)) {
+ try {
+ generateResponse(
+ req != null ? StaplerRequest.toStaplerRequest2(req) : null,
+ rsp != null ? StaplerResponse.toStaplerResponse2(rsp) : null,
+ node);
+ } catch (ServletException e) {
+ throw ServletExceptionWrapper.fromJakartaServletException(e);
+ }
+ } else {
+ throw new AbstractMethodError("The class " + getClass().getName() + " must override at least one of the "
+ + HttpResponse.class.getSimpleName() + ".generateResponse methods");
+ }
+ }
}
diff --git a/core/src/main/java/org/kohsuke/stapler/HttpResponseRenderer.java b/core/src/main/java/org/kohsuke/stapler/HttpResponseRenderer.java
index b01fd35e6..8ef8d7217 100644
--- a/core/src/main/java/org/kohsuke/stapler/HttpResponseRenderer.java
+++ b/core/src/main/java/org/kohsuke/stapler/HttpResponseRenderer.java
@@ -23,12 +23,12 @@
package org.kohsuke.stapler;
+import jakarta.servlet.ServletException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
-import javax.servlet.ServletException;
import net.sf.json.JSON;
import net.sf.json.JSONArray;
import net.sf.json.JSONException;
@@ -58,7 +58,7 @@ public abstract class HttpResponseRenderer {
* false otherwise, in which case the next {@link HttpResponseRenderer}
* will be consulted.
*/
- public abstract boolean generateResponse(StaplerRequest req, StaplerResponse rsp, Object node, Object response)
+ public abstract boolean generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node, Object response)
throws IOException, ServletException;
/**
@@ -66,7 +66,7 @@ public abstract boolean generateResponse(StaplerRequest req, StaplerResponse rsp
*/
public static class Default extends HttpResponseRenderer {
@Override
- public boolean generateResponse(StaplerRequest req, StaplerResponse rsp, Object node, Object response)
+ public boolean generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node, Object response)
throws IOException, ServletException {
return handleHttpResponse(req, rsp, node, response)
|| handleJSON(rsp, response)
@@ -74,7 +74,7 @@ public boolean generateResponse(StaplerRequest req, StaplerResponse rsp, Object
|| handlePrimitive(rsp, response);
}
- protected boolean handleJavaScriptProxyMethodCall(StaplerRequest req, StaplerResponse rsp, Object response)
+ protected boolean handleJavaScriptProxyMethodCall(StaplerRequest2 req, StaplerResponse2 rsp, Object response)
throws IOException {
if (req.isJavaScriptProxyCall()) {
rsp.setContentType(Flavor.JSON.contentType);
@@ -112,7 +112,7 @@ protected boolean handleJavaScriptProxyMethodCall(StaplerRequest req, StaplerRes
return false;
}
- protected boolean handlePrimitive(StaplerResponse rsp, Object response) throws IOException {
+ protected boolean handlePrimitive(StaplerResponse2 rsp, Object response) throws IOException {
if (response instanceof String || response instanceof Integer) {
rsp.setContentType("text/plain;charset=UTF-8");
rsp.getWriter().print(response);
@@ -121,7 +121,7 @@ protected boolean handlePrimitive(StaplerResponse rsp, Object response) throws I
return false;
}
- protected boolean handleHttpResponse(StaplerRequest req, StaplerResponse rsp, Object node, Object response)
+ protected boolean handleHttpResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node, Object response)
throws IOException, ServletException {
if (response instanceof HttpResponse r) {
// let the result render the response
@@ -137,7 +137,7 @@ protected boolean handleHttpResponse(StaplerRequest req, StaplerResponse rsp, Ob
return false;
}
- protected boolean handleJSON(StaplerResponse rsp, Object response) throws IOException {
+ protected boolean handleJSON(StaplerResponse2 rsp, Object response) throws IOException {
if (response instanceof JSON) {
rsp.setContentType(Flavor.JSON.contentType);
((JSON) response).write(rsp.getWriter());
diff --git a/core/src/main/java/org/kohsuke/stapler/HttpResponses.java b/core/src/main/java/org/kohsuke/stapler/HttpResponses.java
index aa8a8a97e..24a27b0bb 100644
--- a/core/src/main/java/org/kohsuke/stapler/HttpResponses.java
+++ b/core/src/main/java/org/kohsuke/stapler/HttpResponses.java
@@ -25,12 +25,12 @@
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
import java.util.ServiceLoader;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletResponse;
/**
* Factory for {@link HttpResponse}.
@@ -75,7 +75,7 @@ public static HttpResponseException forbidden() {
public static HttpResponseException status(final int code) {
return new HttpResponseException() {
@Override
- public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node)
+ public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node)
throws IOException, ServletException {
rsp.setStatus(code);
}
@@ -108,7 +108,7 @@ public static HttpResponseException error(final int code, final Throwable cause)
@SuppressFBWarnings(
value = "INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE",
justification = "Jenkins handles this issue differently or doesn't care about it")
- public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node)
+ public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node)
throws IOException, ServletException {
rsp.setStatus(code);
@@ -130,7 +130,7 @@ public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object nod
public static HttpResponseException errorWithoutStack(final int code, final String errorMessage) {
return new HttpResponseException() {
@Override
- public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node)
+ public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node)
throws IOException, ServletException {
rsp.sendError(code, errorMessage);
}
@@ -144,7 +144,7 @@ public static HttpResponseException wrap(HttpResponse delegate) {
if (delegate instanceof Throwable) {
return new HttpResponseException(((Throwable) delegate).getMessage(), (Throwable) delegate) {
@Override
- public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node)
+ public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node)
throws IOException, ServletException {
delegate.generateResponse(req, rsp, node);
}
@@ -152,7 +152,7 @@ public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object nod
} else {
return new HttpResponseException() {
@Override
- public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node)
+ public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node)
throws IOException, ServletException {
delegate.generateResponse(req, rsp, node);
}
@@ -172,7 +172,7 @@ public static HttpResponseException redirectViaContextPath(String relative) {
public static HttpResponseException redirectViaContextPath(final int statusCode, final String relative) {
return new HttpResponseException() {
@Override
- public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node)
+ public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node)
throws IOException, ServletException {
StringBuilder sb = new StringBuilder(req.getContextPath());
if (!relative.startsWith("/")) {
@@ -220,7 +220,7 @@ public static HttpResponseException forwardToPreviousPage() {
private static final HttpResponseException FORWARD_TO_PREVIOUS_PAGE = new HttpResponseException() {
@Override
- public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node)
+ public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node)
throws IOException, ServletException {
rsp.forwardToPreviousPage(req);
}
@@ -250,7 +250,7 @@ public static HttpResponse staticResource(URL resource) {
public static HttpResponse staticResource(final URL resource, final long expiration) {
return new HttpResponse() {
@Override
- public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node)
+ public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node)
throws IOException, ServletException {
rsp.serveFile(req, resource, expiration);
}
@@ -264,7 +264,7 @@ public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object nod
public static HttpResponse html(final String literalHtml) {
return new HttpResponse() {
@Override
- public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node)
+ public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node)
throws IOException, ServletException {
rsp.setContentType("text/html;charset=UTF-8");
rsp.getWriter().println(literalHtml);
@@ -278,7 +278,7 @@ public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object nod
public static HttpResponse literalHtml(final String text) {
return new HttpResponse() {
@Override
- public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node)
+ public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node)
throws IOException, ServletException {
rsp.setContentType("text/html;charset=UTF-8");
PrintWriter pw = rsp.getWriter();
@@ -295,7 +295,7 @@ public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object nod
public static HttpResponse plainText(final String plainText) {
return new HttpResponse() {
@Override
- public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node)
+ public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node)
throws IOException, ServletException {
rsp.setContentType("text/plain;charset=UTF-8");
rsp.getWriter().println(plainText);
@@ -309,7 +309,7 @@ public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object nod
public static HttpResponse text(final String text) {
return new HttpResponse() {
@Override
- public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node)
+ public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node)
throws IOException, ServletException {
rsp.setContentType("text/plain;charset=UTF-8");
PrintWriter pw = rsp.getWriter();
diff --git a/core/src/main/java/org/kohsuke/stapler/IndexDispatcher.java b/core/src/main/java/org/kohsuke/stapler/IndexDispatcher.java
index f25266947..9501581e6 100644
--- a/core/src/main/java/org/kohsuke/stapler/IndexDispatcher.java
+++ b/core/src/main/java/org/kohsuke/stapler/IndexDispatcher.java
@@ -1,8 +1,8 @@
package org.kohsuke.stapler;
+import jakarta.servlet.ServletException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
-import javax.servlet.ServletException;
/**
* {@link Dispatcher} for url=/ that handles the tail of an URL.
diff --git a/core/src/main/java/org/kohsuke/stapler/IndexHtmlDispatcher.java b/core/src/main/java/org/kohsuke/stapler/IndexHtmlDispatcher.java
index 6e3ba1eec..2d5002bc1 100644
--- a/core/src/main/java/org/kohsuke/stapler/IndexHtmlDispatcher.java
+++ b/core/src/main/java/org/kohsuke/stapler/IndexHtmlDispatcher.java
@@ -1,10 +1,10 @@
package org.kohsuke.stapler;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
/**
* Serve {@code index.html} from {@code WEB-INF/side-files} if that exists.
diff --git a/core/src/main/java/org/kohsuke/stapler/IndexViewDispatcher.java b/core/src/main/java/org/kohsuke/stapler/IndexViewDispatcher.java
index af3dd973e..1e47866bd 100644
--- a/core/src/main/java/org/kohsuke/stapler/IndexViewDispatcher.java
+++ b/core/src/main/java/org/kohsuke/stapler/IndexViewDispatcher.java
@@ -1,8 +1,8 @@
package org.kohsuke.stapler;
+import jakarta.servlet.ServletException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
-import javax.servlet.ServletException;
/**
* {@link Dispatcher} that deals with the "index" view pages that are used when the request path doesn't contain
diff --git a/core/src/main/java/org/kohsuke/stapler/LimitedTo.java b/core/src/main/java/org/kohsuke/stapler/LimitedTo.java
index 51c82c0d0..71a21722e 100644
--- a/core/src/main/java/org/kohsuke/stapler/LimitedTo.java
+++ b/core/src/main/java/org/kohsuke/stapler/LimitedTo.java
@@ -23,12 +23,12 @@
package org.kohsuke.stapler;
+import jakarta.servlet.ServletException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
-import javax.servlet.ServletException;
import org.kohsuke.stapler.interceptor.Interceptor;
import org.kohsuke.stapler.interceptor.InterceptorAnnotation;
@@ -63,7 +63,7 @@ public void setTarget(Function target) {
}
@Override
- public Object invoke(StaplerRequest request, StaplerResponse response, Object instance, Object[] arguments)
+ public Object invoke(StaplerRequest2 request, StaplerResponse2 response, Object instance, Object[] arguments)
throws IllegalAccessException, InvocationTargetException, ServletException {
if (request.isUserInRole(role)) {
return target.invoke(request, response, instance, arguments);
diff --git a/core/src/main/java/org/kohsuke/stapler/LocaleDrivenResourceProvider.java b/core/src/main/java/org/kohsuke/stapler/LocaleDrivenResourceProvider.java
index c82b2282d..f8cadf6b3 100644
--- a/core/src/main/java/org/kohsuke/stapler/LocaleDrivenResourceProvider.java
+++ b/core/src/main/java/org/kohsuke/stapler/LocaleDrivenResourceProvider.java
@@ -35,7 +35,7 @@
/**
* Service provider interface allowing to hook into webapp resource lookup.
*
- * This cannot be made a property of WebApp as other behavior customizations, as webapp resource lookup is done before we have StaplerRequest/StaplerResponse.
+ * This cannot be made a property of WebApp as other behavior customizations, as webapp resource lookup is done before we have StaplerRequest2/StaplerResponse2.
*/
public abstract class LocaleDrivenResourceProvider {
/**
diff --git a/core/src/main/java/org/kohsuke/stapler/MetaClass.java b/core/src/main/java/org/kohsuke/stapler/MetaClass.java
index f9511b1df..92e2903bf 100644
--- a/core/src/main/java/org/kohsuke/stapler/MetaClass.java
+++ b/core/src/main/java/org/kohsuke/stapler/MetaClass.java
@@ -25,6 +25,8 @@
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.annotation.PostConstruct;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
@@ -33,8 +35,6 @@
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONArray;
import org.apache.commons.io.IOUtils;
import org.kohsuke.stapler.bind.JavaScriptMethod;
@@ -156,7 +156,7 @@ public class MetaClass extends TearOffSupport {
f.buildIndexDispatchers(this, dispatchers);
}
- Dispatcher d = IndexHtmlDispatcher.make(webApp.context, clazz);
+ Dispatcher d = IndexHtmlDispatcher.make(webApp.getServletContext(), clazz);
if (d != null) {
dispatchers.add(d);
}
@@ -264,8 +264,8 @@ public String toString() {
});
}
- // check public selector methods of the form static NODE.getTOKEN(StaplerRequest)
- for (final Function f : getMethods.signature(StaplerRequest.class)) {
+ // check public selector methods of the form static NODE.getTOKEN(StaplerRequest2)
+ for (final Function f : getMethods.signature(StaplerRequest2.class)) {
if (f.getName().length() <= 3) {
continue;
}
@@ -290,6 +290,48 @@ public boolean doDispatch(RequestImpl req, ResponseImpl rsp, Object node)
}
}
+ @Override
+ public String toString() {
+ if (isAccepted) {
+ return String.format(
+ "%3$s %1$s(StaplerRequest2) for url=/%2$s/...",
+ ff.getQualifiedName(), name, ff.getReturnType().getName());
+ } else {
+ return String.format(
+ "BLOCKED: %3$s %1$s(StaplerRequest2) for url=/%2$s/...",
+ ff.getQualifiedName(), name, ff.getReturnType().getName());
+ }
+ }
+ });
+ }
+
+ // check public selector methods of the deprecated form static NODE.getTOKEN(StaplerRequest)
+ for (final Function f : getMethods.signature(StaplerRequest.class)) {
+ if (f.getName().length() <= 3) {
+ continue;
+ }
+ String name = camelize(f.getName().substring(3)); // 'getFoo' -> 'foo'
+ final Function ff = f.contextualize(new TraversalMethodContext(name));
+ final boolean isAccepted = filteredGetMethods.contains(f);
+
+ dispatchers.add(new NameBasedDispatcher(name) {
+ @Override
+ public boolean doDispatch(RequestImpl req, ResponseImpl rsp, Object node)
+ throws IOException, ServletException, IllegalAccessException, InvocationTargetException {
+ if (isAccepted) {
+ Dispatcher.anonymizedTraceEval(req, rsp, node, "%s#%s(...)", ff.getName());
+ if (traceable()) {
+ traceEval(req, rsp, node, ff.getName() + "(...)");
+ }
+ req.getStapler()
+ .invoke(req, rsp, ff.invoke(req, rsp, node, StaplerRequest.fromStaplerRequest2(req)));
+ return true;
+ } else {
+ return webApp.getFilteredGetterTriggerListener()
+ .onGetterTrigger(f, req, rsp, node, ff.getName() + "(...)");
+ }
+ }
+
@Override
public String toString() {
if (isAccepted) {
@@ -560,7 +602,7 @@ public boolean dispatch(RequestImpl req, ResponseImpl rsp, Object node)
@Override
public String toString() {
return String.format(
- "%2$s %s(String,StaplerRequest,StaplerResponse) for url=/TOKEN/...",
+ "%2$s %s(String,StaplerRequest[2],StaplerResponse[2]) for url=/TOKEN/...",
ff.getQualifiedName(), ff.getReturnType().getName());
}
});
@@ -582,7 +624,7 @@ public boolean dispatch(RequestImpl req, ResponseImpl rsp, Object node)
@Override
public String toString() {
- return String.format("%s(StaplerRequest,StaplerResponse) for any URL", ff.getQualifiedName());
+ return String.format("%s(StaplerRequest[2],StaplerResponse[2]) for any URL", ff.getQualifiedName());
}
});
}
diff --git a/core/src/main/java/org/kohsuke/stapler/NameBasedDispatcher.java b/core/src/main/java/org/kohsuke/stapler/NameBasedDispatcher.java
index 30a21b3d6..050920d83 100644
--- a/core/src/main/java/org/kohsuke/stapler/NameBasedDispatcher.java
+++ b/core/src/main/java/org/kohsuke/stapler/NameBasedDispatcher.java
@@ -23,9 +23,9 @@
package org.kohsuke.stapler;
+import jakarta.servlet.ServletException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
-import javax.servlet.ServletException;
/**
* @author Kohsuke Kawaguchi
diff --git a/core/src/main/java/org/kohsuke/stapler/PreInvokeInterceptedFunction.java b/core/src/main/java/org/kohsuke/stapler/PreInvokeInterceptedFunction.java
index 2f39f908a..55ea851da 100644
--- a/core/src/main/java/org/kohsuke/stapler/PreInvokeInterceptedFunction.java
+++ b/core/src/main/java/org/kohsuke/stapler/PreInvokeInterceptedFunction.java
@@ -1,7 +1,7 @@
package org.kohsuke.stapler;
+import jakarta.servlet.ServletException;
import java.lang.reflect.InvocationTargetException;
-import javax.servlet.ServletException;
import org.kohsuke.stapler.interceptor.Interceptor;
import org.kohsuke.stapler.interceptor.InterceptorAnnotation;
import org.kohsuke.stapler.interceptor.Stage;
@@ -21,7 +21,7 @@ final class PreInvokeInterceptedFunction extends ForwardingFunction {
}
@Override
- public Object invoke(StaplerRequest req, StaplerResponse rsp, Object o, Object... args)
+ public Object invoke(StaplerRequest2 req, StaplerResponse2 rsp, Object o, Object... args)
throws IllegalAccessException, InvocationTargetException, ServletException {
return interceptor.invoke(req, rsp, o, args);
}
diff --git a/core/src/main/java/org/kohsuke/stapler/QueryParameter.java b/core/src/main/java/org/kohsuke/stapler/QueryParameter.java
index 6f062399d..d1f8f3e74 100644
--- a/core/src/main/java/org/kohsuke/stapler/QueryParameter.java
+++ b/core/src/main/java/org/kohsuke/stapler/QueryParameter.java
@@ -23,12 +23,12 @@
package org.kohsuke.stapler;
+import jakarta.servlet.ServletException;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import javax.servlet.ServletException;
import org.kohsuke.stapler.QueryParameter.HandlerImpl;
/**
@@ -60,7 +60,7 @@
class HandlerImpl extends AnnotationHandler {
@Override
- public Object parse(StaplerRequest request, QueryParameter a, Class type, String parameterName)
+ public Object parse(StaplerRequest2 request, QueryParameter a, Class type, String parameterName)
throws ServletException {
String name = a.value();
if (name.isEmpty()) {
diff --git a/core/src/main/java/org/kohsuke/stapler/ReflectionUtils.java b/core/src/main/java/org/kohsuke/stapler/ReflectionUtils.java
index b21389913..929d16eb1 100644
--- a/core/src/main/java/org/kohsuke/stapler/ReflectionUtils.java
+++ b/core/src/main/java/org/kohsuke/stapler/ReflectionUtils.java
@@ -23,12 +23,17 @@
package org.kohsuke.stapler;
+import edu.umd.cs.findbugs.annotations.NonNull;
+import edu.umd.cs.findbugs.annotations.Nullable;
import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Supplier;
/**
* @author Kohsuke Kawaguchi
@@ -81,4 +86,112 @@ public static Annotation[] union(Annotation[] a, Annotation[] b) {
return combined.toArray(new Annotation[0]);
}
+
+ /**
+ * Checks whether the method defined on the base type with the given arguments is overridden in the given derived
+ * type.
+ *
+ * @param base The base type.
+ * @param derived The derived type.
+ * @param methodName The name of the method.
+ * @param types The types of the arguments for the method.
+ * @return {@code true} when {@code derived} provides the specified method other than as inherited from {@code base}.
+ * @throws IllegalArgumentException When {@code derived} does not derive from {@code base}, or when {@code base}
+ * does not contain the specified method.
+ */
+ public static boolean isOverridden(
+ @NonNull Class> base, @NonNull Class> derived, @NonNull String methodName, @NonNull Class>... types) {
+ // If derived is not a subclass or implementor of base, it can't override any method
+ // Technically this should also be triggered when base == derived, because it can't override its own method, but
+ // the unit tests explicitly test for that as working.
+ if (!base.isAssignableFrom(derived)) {
+ throw new IllegalArgumentException("The specified derived class (" + derived.getCanonicalName()
+ + ") does not derive from the specified base class (" + base.getCanonicalName() + ").");
+ }
+ final Method baseMethod = getMethod(base, null, methodName, types);
+ if (baseMethod == null) {
+ throw new IllegalArgumentException("The specified method is not declared by the specified base class ("
+ + base.getCanonicalName() + "), or it is private, static or final.");
+ }
+ final Method derivedMethod = getMethod(derived, base, methodName, types);
+ // the lookup will either return null or the base method when no override has been found (depending on whether
+ // the base is an interface)
+ return derivedMethod != null && derivedMethod != baseMethod;
+ }
+
+ /**
+ * Calls the given supplier if the method defined on the base type with the given arguments is overridden in the
+ * given derived type.
+ *
+ * @param supplier The supplier to call if the method is indeed overridden.
+ * @param base The base type.
+ * @param derived The derived type.
+ * @param methodName The name of the method.
+ * @param types The types of the arguments for the method.
+ * @return {@code true} when {@code derived} provides the specified method other than as inherited from {@code base}.
+ * @throws IllegalArgumentException When {@code derived} does not derive from {@code base}, or when {@code base}
+ * does not contain the specified method.
+ * @throws AbstractMethodError If the derived class doesn't override the given method.
+ * @since 2.259
+ */
+ public static T ifOverridden(
+ Supplier supplier,
+ @NonNull Class> base,
+ @NonNull Class> derived,
+ @NonNull String methodName,
+ @NonNull Class>... types) {
+ if (isOverridden(base, derived, methodName, types)) {
+ return supplier.get();
+ } else {
+ throw new AbstractMethodError("The class " + derived.getName() + " must override at least one of the "
+ + base.getSimpleName() + "." + methodName + " methods");
+ }
+ }
+
+ private static Method getMethod(
+ @NonNull Class> clazz, @Nullable Class> base, @NonNull String methodName, @NonNull Class>... types) {
+ try {
+ final Method res = clazz.getDeclaredMethod(methodName, types);
+ final int mod = res.getModifiers();
+ // private and static methods are never ok, and end the search
+ if (Modifier.isPrivate(mod) || Modifier.isStatic(mod)) {
+ return null;
+ }
+ // when looking for the base/declaring method, final is not ok
+ if (base == null && Modifier.isFinal(mod)) {
+ return null;
+ }
+ // when looking for the overriding method, abstract is not ok
+ if (base != null && Modifier.isAbstract(mod)) {
+ return null;
+ }
+ return res;
+ } catch (NoSuchMethodException e) {
+ // If the base is an interface, the implementation may come from a default implementation on a derived
+ // interface. So look at interfaces too.
+ if (base != null && Modifier.isInterface(base.getModifiers())) {
+ for (Class> iface : clazz.getInterfaces()) {
+ if (base.equals(iface) || !base.isAssignableFrom(iface)) {
+ continue;
+ }
+ final Method defaultImpl = getMethod(iface, base, methodName, types);
+ if (defaultImpl != null) {
+ return defaultImpl;
+ }
+ }
+ }
+ // Method not found in clazz, let's search in superclasses
+ Class> superclass = clazz.getSuperclass();
+ if (superclass != null) {
+ // if the superclass doesn't derive from base anymore (or IS base), stop looking
+ if (base != null && (base.equals(superclass) || !base.isAssignableFrom(superclass))) {
+ return null;
+ }
+ return getMethod(superclass, base, methodName, types);
+ }
+ return null;
+ } catch (SecurityException e) {
+ throw new AssertionError(e);
+ }
+ }
}
diff --git a/core/src/main/java/org/kohsuke/stapler/RequestImpl.java b/core/src/main/java/org/kohsuke/stapler/RequestImpl.java
index 12ae78720..1c7c89572 100644
--- a/core/src/main/java/org/kohsuke/stapler/RequestImpl.java
+++ b/core/src/main/java/org/kohsuke/stapler/RequestImpl.java
@@ -23,6 +23,12 @@
package org.kohsuke.stapler;
+import jakarta.servlet.RequestDispatcher;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequestWrapper;
+import jakarta.servlet.http.HttpServletResponse;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.File;
@@ -54,12 +60,6 @@
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
-import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSON;
import net.sf.json.JSONArray;
import net.sf.json.JSONException;
@@ -76,8 +76,8 @@
import org.apache.commons.fileupload2.core.FileUploadException;
import org.apache.commons.fileupload2.core.FileUploadFileCountLimitException;
import org.apache.commons.fileupload2.core.FileUploadSizeException;
-import org.apache.commons.fileupload2.javax.JavaxServletDiskFileUpload;
-import org.apache.commons.fileupload2.javax.JavaxServletFileUpload;
+import org.apache.commons.fileupload2.jakarta.servlet5.JakartaServletDiskFileUpload;
+import org.apache.commons.fileupload2.jakarta.servlet5.JakartaServletFileUpload;
import org.jvnet.tiger_types.Lister;
import org.kohsuke.stapler.bind.Bound;
import org.kohsuke.stapler.bind.BoundObjectTable;
@@ -86,11 +86,11 @@
import org.kohsuke.stapler.util.IllegalReflectiveAccessLogHandler;
/**
- * {@link StaplerRequest} implementation.
+ * {@link StaplerRequest2} implementation.
*
* @author Kohsuke Kawaguchi
*/
-public class RequestImpl extends HttpServletRequestWrapper implements StaplerRequest {
+public class RequestImpl extends HttpServletRequestWrapper implements StaplerRequest2 {
/**
* Tokenized URLs and consumed tokens.
* This object is modified by {@link Stapler} as we parse through the URL.
@@ -137,7 +137,7 @@ public class RequestImpl extends HttpServletRequestWrapper implements StaplerReq
/**
* Limits the number of form fields that can be processed in one multipart/form-data request.
- * Used to set {@link org.apache.commons.fileupload2.javax.JavaxServletFileUpload#setFileCountMax(long)}.
+ * Used to set {@link org.apache.commons.fileupload2.jakarta.servlet5.JakartaServletFileUpload#setFileCountMax(long)}.
* Despite the name, this applies to all form fields, not just actual file attachments.
* Set to {@code -1} to disable limits.
*/
@@ -146,7 +146,7 @@ public class RequestImpl extends HttpServletRequestWrapper implements StaplerReq
/**
* Limits the size (in bytes) of individual fields that can be processed in one multipart/form-data request.
- * Used to set {@link org.apache.commons.fileupload2.javax.JavaxServletFileUpload#setFileSizeMax(long)}.
+ * Used to set {@link org.apache.commons.fileupload2.jakarta.servlet5.JakartaServletFileUpload#setFileSizeMax(long)}.
* Despite the name, this applies to all form fields, not just actual file attachments.
* Set to {@code -1} to disable limits.
*/
@@ -155,7 +155,7 @@ public class RequestImpl extends HttpServletRequestWrapper implements StaplerReq
/**
* Limits the total request size (in bytes) that can be processed in one multipart/form-data request.
- * Used to set {@link org.apache.commons.fileupload2.javax.JavaxServletFileUpload#setSizeMax(long)}.
+ * Used to set {@link org.apache.commons.fileupload2.jakarta.servlet5.JakartaServletFileUpload#setSizeMax(long)}.
* Set to {@code -1} to disable limits.
*/
private static /* nonfinal for Jenkins script console */ long FILEUPLOAD_MAX_SIZE =
@@ -178,6 +178,22 @@ public RequestImpl(Stapler stapler, HttpServletRequest request, List ancestors,
+ TokenList tokens) {
+ this(
+ stapler,
+ io.jenkins.servlet.http.HttpServletRequestWrapper.toJakartaHttpServletRequest(request),
+ ancestors,
+ tokens);
+ }
+
@Override
public boolean isJavaScriptProxyCall() {
String ct = getContentType();
@@ -408,12 +424,12 @@ public String getOriginalRequestURI() {
}
@Override
- public boolean checkIfModified(long lastModified, StaplerResponse rsp) {
+ public boolean checkIfModified(long lastModified, StaplerResponse2 rsp) {
return checkIfModified(lastModified, rsp, 0);
}
@Override
- public boolean checkIfModified(long lastModified, StaplerResponse rsp, long expiration) {
+ public boolean checkIfModified(long lastModified, StaplerResponse2 rsp, long expiration) {
if (lastModified <= 0) {
return false;
}
@@ -446,12 +462,12 @@ public boolean checkIfModified(long lastModified, StaplerResponse rsp, long expi
}
@Override
- public boolean checkIfModified(Date timestampOfResource, StaplerResponse rsp) {
+ public boolean checkIfModified(Date timestampOfResource, StaplerResponse2 rsp) {
return checkIfModified(timestampOfResource.getTime(), rsp);
}
@Override
- public boolean checkIfModified(Calendar timestampOfResource, StaplerResponse rsp) {
+ public boolean checkIfModified(Calendar timestampOfResource, StaplerResponse2 rsp) {
return checkIfModified(timestampOfResource.getTimeInMillis(), rsp);
}
@@ -978,7 +994,7 @@ private Object instantiate(Class actualType, JSONObject j) {
}
/**
- * Calls {@link DataBoundResolvable#bindResolve(StaplerRequest, JSONObject)} if the object has it.
+ * Calls {@link DataBoundResolvable#bindResolve(StaplerRequest2, JSONObject)} if the object has it.
*/
private Object bindResolve(Object o, JSONObject src) {
if (o instanceof DataBoundResolvable dbr) {
@@ -1153,7 +1169,7 @@ private void parseMultipartFormData() throws ServletException {
parsedFormData = new HashMap<>();
parsedFormDataFormFields = new HashMap<>();
- JavaxServletFileUpload upload;
+ JakartaServletFileUpload upload;
File tmpDir;
try {
tmpDir = Files.createTempDirectory("jenkins-stapler-uploads").toFile();
@@ -1161,7 +1177,7 @@ private void parseMultipartFormData() throws ServletException {
throw new ServletException("Error creating temporary directory", e);
}
tmpDir.deleteOnExit();
- upload = new JavaxServletDiskFileUpload(
+ upload = new JakartaServletDiskFileUpload(
DiskFileItemFactory.builder().setFile(tmpDir).get());
upload.setFileCountMax(FILEUPLOAD_MAX_FILES);
upload.setFileSizeMax(FILEUPLOAD_MAX_FILE_SIZE);
@@ -1250,7 +1266,7 @@ public JSONObject getSubmittedForm() throws ServletException {
if (p == null || p.isEmpty()) {
// no data submitted
try {
- StaplerResponse rsp = Stapler.getCurrentResponse();
+ StaplerResponse2 rsp = Stapler.getCurrentResponse2();
if (isSubmission) {
rsp.sendError(HttpServletResponse.SC_BAD_REQUEST, "This page expects a form submission");
} else {
diff --git a/core/src/main/java/org/kohsuke/stapler/ResponseImpl.java b/core/src/main/java/org/kohsuke/stapler/ResponseImpl.java
index 98a00ec72..1ac68ece1 100644
--- a/core/src/main/java/org/kohsuke/stapler/ResponseImpl.java
+++ b/core/src/main/java/org/kohsuke/stapler/ResponseImpl.java
@@ -25,6 +25,11 @@
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletOutputStream;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -39,11 +44,6 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpServletResponseWrapper;
import net.sf.json.JsonConfig;
import org.apache.commons.io.IOUtils;
import org.kohsuke.stapler.export.DataWriter;
@@ -56,11 +56,11 @@
import org.kohsuke.stapler.export.TreePruner.ByDepth;
/**
- * {@link StaplerResponse} implementation.
+ * {@link StaplerResponse2} implementation.
*
* @author Kohsuke Kawaguchi
*/
-public class ResponseImpl extends HttpServletResponseWrapper implements StaplerResponse {
+public class ResponseImpl extends HttpServletResponseWrapper implements StaplerResponse2 {
private final Stapler stapler;
private final HttpServletResponse response;
@@ -85,6 +85,14 @@ public ResponseImpl(Stapler stapler, HttpServletResponse response) {
this.response = response;
}
+ /**
+ * @deprecated use {@link #ResponseImpl(Stapler, HttpServletResponse)}
+ */
+ @Deprecated
+ public ResponseImpl(Stapler stapler, javax.servlet.http.HttpServletResponse response) {
+ this(stapler, io.jenkins.servlet.http.HttpServletResponseWrapper.toJakartaHttpServletResponse(response));
+ }
+
@Override
public ServletOutputStream getOutputStream() throws IOException {
if (mode == OutputMode.CHAR) {
@@ -124,12 +132,12 @@ private T recordOutput(T obj) {
}
@Override
- public void forward(Object it, String url, StaplerRequest request) throws ServletException, IOException {
+ public void forward(Object it, String url, StaplerRequest2 request) throws ServletException, IOException {
stapler.invoke(request, response, it, url);
}
@Override
- public void forwardToPreviousPage(StaplerRequest request) throws ServletException, IOException {
+ public void forwardToPreviousPage(StaplerRequest2 request) throws ServletException, IOException {
String referer = request.getHeader("Referer");
if (referer == null) {
referer = ".";
@@ -149,7 +157,7 @@ public void sendRedirect(@NonNull String url) throws IOException {
}
// example: /foo/bar/zot + ../abc -> /foo/bar/../abc
- String base = Stapler.getCurrentRequest().getRequestURI();
+ String base = Stapler.getCurrentRequest2().getRequestURI();
base = base.substring(0, base.lastIndexOf('/') + 1);
if (!url.equals(".")) {
base += url;
@@ -175,7 +183,7 @@ public void sendRedirect(int statusCode, @NonNull String url) throws IOException
// absolute URLs
url = encode(url);
} else {
- StaplerRequest req = Stapler.getCurrentRequest();
+ StaplerRequest2 req = Stapler.getCurrentRequest2();
if (!url.startsWith("/")) {
// WebSphere doesn't apparently handle relative URLs, so
@@ -213,24 +221,24 @@ private void setLocation(@NonNull String url) {
}
@Override
- public void serveFile(StaplerRequest req, URL resource, long expiration) throws ServletException, IOException {
+ public void serveFile(StaplerRequest2 req, URL resource, long expiration) throws ServletException, IOException {
if (!stapler.serveStaticResource(req, this, resource, expiration)) {
sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
@Override
- public void serveFile(StaplerRequest req, URL resource) throws ServletException, IOException {
+ public void serveFile(StaplerRequest2 req, URL resource) throws ServletException, IOException {
serveFile(req, resource, -1);
}
@Override
- public void serveLocalizedFile(StaplerRequest request, URL res) throws ServletException, IOException {
+ public void serveLocalizedFile(StaplerRequest2 request, URL res) throws ServletException, IOException {
serveLocalizedFile(request, res, -1);
}
@Override
- public void serveLocalizedFile(StaplerRequest request, URL res, long expiration)
+ public void serveLocalizedFile(StaplerRequest2 request, URL res, long expiration)
throws ServletException, IOException {
if (!stapler.serveStaticResource(
request, this, stapler.selectResourceByLocale(res, request.getLocale()), expiration)) {
@@ -240,7 +248,7 @@ public void serveLocalizedFile(StaplerRequest request, URL res, long expiration)
@Override
public void serveFile(
- StaplerRequest req,
+ StaplerRequest2 req,
InputStream data,
long lastModified,
long expiration,
@@ -254,7 +262,7 @@ public void serveFile(
@Override
public void serveFile(
- StaplerRequest req,
+ StaplerRequest2 req,
InputStream data,
long lastModified,
long expiration,
@@ -265,27 +273,27 @@ public void serveFile(
}
@Override
- public void serveFile(StaplerRequest req, InputStream data, long lastModified, long contentLength, String fileName)
+ public void serveFile(StaplerRequest2 req, InputStream data, long lastModified, long contentLength, String fileName)
throws ServletException, IOException {
serveFile(req, data, lastModified, -1, contentLength, fileName);
}
@Override
- public void serveFile(StaplerRequest req, InputStream data, long lastModified, int contentLength, String fileName)
+ public void serveFile(StaplerRequest2 req, InputStream data, long lastModified, int contentLength, String fileName)
throws ServletException, IOException {
serveFile(req, data, lastModified, (long) contentLength, fileName);
}
@Override
@SuppressWarnings({"unchecked", "rawtypes"}) // API design flaw prevents this from type-checking
- public void serveExposedBean(StaplerRequest req, Object exposedBean, Flavor flavor)
+ public void serveExposedBean(StaplerRequest2 req, Object exposedBean, Flavor flavor)
throws ServletException, IOException {
serveExposedBean(
req, exposedBean, new ExportConfig().withFlavor(flavor).withPrettyPrint(req.hasParameter("pretty")));
}
@Override
- public void serveExposedBean(StaplerRequest req, Object exposedBean, ExportConfig config)
+ public void serveExposedBean(StaplerRequest2 req, Object exposedBean, ExportConfig config)
throws ServletException, IOException {
String pad = null;
Flavor flavor = config.getFlavor();
@@ -355,7 +363,7 @@ public Writer getCompressedWriter(HttpServletRequest req) throws IOException {
}
@Override
- public int reverseProxyTo(URL url, StaplerRequest req) throws IOException {
+ public int reverseProxyTo(URL url, StaplerRequest2 req) throws IOException {
HttpURLConnection con = openConnection(url);
con.setDoOutput(true);
diff --git a/core/src/main/java/org/kohsuke/stapler/ScriptExecutor.java b/core/src/main/java/org/kohsuke/stapler/ScriptExecutor.java
index 9a54a6f60..3d63ae5a1 100644
--- a/core/src/main/java/org/kohsuke/stapler/ScriptExecutor.java
+++ b/core/src/main/java/org/kohsuke/stapler/ScriptExecutor.java
@@ -38,6 +38,7 @@ public interface ScriptExecutor {
/**
* Executes the given script on the given node and request, rendering output to the given response.
*/
- void execute(@NonNull StaplerRequest req, @NonNull StaplerResponse rsp, @NonNull S script, @CheckForNull Object it)
+ void execute(
+ @NonNull StaplerRequest2 req, @NonNull StaplerResponse2 rsp, @NonNull S script, @CheckForNull Object it)
throws Exception;
}
diff --git a/core/src/main/java/org/kohsuke/stapler/ScriptRequestDispatcher.java b/core/src/main/java/org/kohsuke/stapler/ScriptRequestDispatcher.java
index 803122772..9c63b6562 100644
--- a/core/src/main/java/org/kohsuke/stapler/ScriptRequestDispatcher.java
+++ b/core/src/main/java/org/kohsuke/stapler/ScriptRequestDispatcher.java
@@ -27,13 +27,13 @@
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import jakarta.servlet.RequestDispatcher;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
/**
* Implements a RequestDispatcher for a given model node and view. Unlike dispatchers created through
@@ -91,8 +91,8 @@ private ScriptRequestDispatcher(
@Override
public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException {
- StaplerRequest req = (StaplerRequest) request;
- StaplerResponse rsp = (StaplerResponse) response;
+ StaplerRequest2 req = (StaplerRequest2) request;
+ StaplerResponse2 rsp = (StaplerResponse2) response;
DispatchValidator validator = req.getWebApp().getDispatchValidator();
validator.allowDispatch(req, rsp);
try {
diff --git a/core/src/main/java/org/kohsuke/stapler/SelectionInterceptedFunction.java b/core/src/main/java/org/kohsuke/stapler/SelectionInterceptedFunction.java
index c1426796d..d8c1a6517 100644
--- a/core/src/main/java/org/kohsuke/stapler/SelectionInterceptedFunction.java
+++ b/core/src/main/java/org/kohsuke/stapler/SelectionInterceptedFunction.java
@@ -1,7 +1,7 @@
package org.kohsuke.stapler;
+import jakarta.servlet.ServletException;
import java.lang.reflect.InvocationTargetException;
-import javax.servlet.ServletException;
import org.kohsuke.stapler.interceptor.Interceptor;
import org.kohsuke.stapler.interceptor.InterceptorAnnotation;
@@ -21,7 +21,7 @@
}
@Override
- Object bindAndInvoke(Object o, StaplerRequest req, StaplerResponse rsp, Object... headArgs)
+ Object bindAndInvoke(Object o, StaplerRequest2 req, StaplerResponse2 rsp, Object... headArgs)
throws IllegalAccessException, InvocationTargetException, ServletException {
return interceptor.invoke(req, rsp, o, headArgs);
}
@@ -32,7 +32,7 @@ private static final class Adapter extends ForwardingFunction {
}
@Override
- public Object invoke(StaplerRequest req, StaplerResponse rsp, Object o, Object... args)
+ public Object invoke(StaplerRequest2 req, StaplerResponse2 rsp, Object o, Object... args)
throws IllegalAccessException, InvocationTargetException, ServletException {
return next.bindAndInvoke(o, req, rsp, args);
}
diff --git a/core/src/main/java/org/kohsuke/stapler/Stapler.java b/core/src/main/java/org/kohsuke/stapler/Stapler.java
index 5cd869a1c..8b12af9da 100644
--- a/core/src/main/java/org/kohsuke/stapler/Stapler.java
+++ b/core/src/main/java/org/kohsuke/stapler/Stapler.java
@@ -24,6 +24,15 @@
package org.kohsuke.stapler;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import jakarta.servlet.RequestDispatcher;
+import jakarta.servlet.ServletConfig;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletContextEvent;
+import jakarta.servlet.ServletContextListener;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.File;
@@ -59,15 +68,6 @@
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletContextEvent;
-import javax.servlet.ServletContextListener;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONObject;
import org.apache.commons.beanutils.ConversionException;
import org.apache.commons.beanutils.ConvertUtils;
@@ -462,7 +462,7 @@ OpenConnection selectResourceByLocale(URL url, Locale locale) throws IOException
/**
* Serves the specified {@link URLConnection} as a static resource.
*/
- boolean serveStaticResource(HttpServletRequest req, StaplerResponse rsp, OpenConnection con, long expiration)
+ boolean serveStaticResource(HttpServletRequest req, StaplerResponse2 rsp, OpenConnection con, long expiration)
throws IOException {
if (con == null) {
return false;
@@ -484,7 +484,7 @@ boolean serveStaticResource(HttpServletRequest req, StaplerResponse rsp, OpenCon
/**
* Serves the specified {@link URL} as a static resource.
*/
- boolean serveStaticResource(HttpServletRequest req, StaplerResponse rsp, URL url, long expiration)
+ boolean serveStaticResource(HttpServletRequest req, StaplerResponse2 rsp, URL url, long expiration)
throws IOException {
return serveStaticResource(req, rsp, openURL(url), expiration);
}
@@ -561,7 +561,7 @@ private static URLConnection openConnection(URL url) throws IOException {
*/
boolean serveStaticResource(
HttpServletRequest req,
- StaplerResponse rsp,
+ StaplerResponse2 rsp,
InputStream in,
long lastModified,
long expiration,
@@ -974,7 +974,7 @@ void invoke(RequestImpl req, ResponseImpl rsp, Object node) throws IOException,
@SuppressFBWarnings(
value = "REQUESTDISPATCHER_FILE_DISCLOSURE",
justification = "Forwarding the request to be handled correctly.")
- public void forward(RequestDispatcher dispatcher, StaplerRequest req, HttpServletResponse rsp)
+ public void forward(RequestDispatcher dispatcher, StaplerRequest2 req, HttpServletResponse rsp)
throws ServletException, IOException {
dispatcher.forward(req, new ResponseImpl(this, rsp));
}
@@ -1018,7 +1018,7 @@ public static void setRoot(ServletContextEvent event, Object rootApp) {
}
/**
- * Sets the classloader used by {@link StaplerRequest#bindJSON(Class, JSONObject)} and its sibling methods.
+ * Sets the classloader used by {@link StaplerRequest2#bindJSON(Class, JSONObject)} and its sibling methods.
*
* @deprecated
* Use {@link WebApp#setClassLoader(ClassLoader)}
@@ -1047,19 +1047,37 @@ public ClassLoader getClassLoader() {
}
/**
- * Gets the current {@link StaplerRequest} that the calling thread is associated with.
+ * Gets the current {@link StaplerRequest2} that the calling thread is associated with.
*/
- public static StaplerRequest getCurrentRequest() {
+ public static StaplerRequest2 getCurrentRequest2() {
return CURRENT_REQUEST.get();
}
/**
- * Gets the current {@link StaplerResponse} that the calling thread is associated with.
+ * @deprecated use {@link #getCurrentRequest2()}
*/
- public static StaplerResponse getCurrentResponse() {
+ @Deprecated
+ public static StaplerRequest getCurrentRequest() {
+ StaplerRequest2 currentRequest = getCurrentRequest2();
+ return currentRequest != null ? StaplerRequest.fromStaplerRequest2(currentRequest) : null;
+ }
+
+ /**
+ * Gets the current {@link StaplerResponse2} that the calling thread is associated with.
+ */
+ public static StaplerResponse2 getCurrentResponse2() {
return CURRENT_RESPONSE.get();
}
+ /**
+ * @deprecated use {@link #getCurrentResponse2()}
+ */
+ @Deprecated
+ public static StaplerResponse getCurrentResponse() {
+ StaplerResponse2 currentResponse = getCurrentResponse2();
+ return currentResponse != null ? StaplerResponse.fromStaplerResponse2(currentResponse) : null;
+ }
+
/**
* Gets the current {@link Stapler} that the calling thread is associated with.
*/
@@ -1133,7 +1151,7 @@ static String canonicalPath(String path) {
/**
* This is the {@link Converter} registry that Stapler uses, primarily
- * for form-to-JSON binding in {@link StaplerRequest#bindJSON(Class, JSONObject)}
+ * for form-to-JSON binding in {@link StaplerRequest2#bindJSON(Class, JSONObject)}
* and its family of methods.
*/
public static final ConvertUtilsBean CONVERT_UTILS = new ConvertUtilsBean();
@@ -1221,7 +1239,7 @@ public FileItem convert(Class type, Object value) {
return null;
}
try {
- return Stapler.getCurrentRequest().getFileItem2(value.toString());
+ return Stapler.getCurrentRequest2().getFileItem2(value.toString());
} catch (ServletException | IOException e) {
throw new ConversionException(e);
}
@@ -1238,7 +1256,7 @@ public org.apache.commons.fileupload.FileItem convert(Class type, Object value)
}
try {
return org.apache.commons.fileupload.FileItem.fromFileUpload2FileItem(
- Stapler.getCurrentRequest().getFileItem2(value.toString()));
+ Stapler.getCurrentRequest2().getFileItem2(value.toString()));
} catch (ServletException | IOException e) {
throw new ConversionException(e);
}
diff --git a/core/src/main/java/org/kohsuke/stapler/StaplerRequest.java b/core/src/main/java/org/kohsuke/stapler/StaplerRequest.java
index 448627d1b..0c0ea358b 100644
--- a/core/src/main/java/org/kohsuke/stapler/StaplerRequest.java
+++ b/core/src/main/java/org/kohsuke/stapler/StaplerRequest.java
@@ -23,17 +23,53 @@
package org.kohsuke.stapler;
+import io.jenkins.servlet.AsyncContextWrapper;
+import io.jenkins.servlet.DispatcherTypeWrapper;
+import io.jenkins.servlet.RequestDispatcherWrapper;
+import io.jenkins.servlet.ServletContextWrapper;
+import io.jenkins.servlet.ServletExceptionWrapper;
+import io.jenkins.servlet.ServletInputStreamWrapper;
+import io.jenkins.servlet.ServletRequestWrapper;
+import io.jenkins.servlet.ServletResponseWrapper;
+import io.jenkins.servlet.http.CookieWrapper;
+import io.jenkins.servlet.http.HttpServletMappingWrapper;
+import io.jenkins.servlet.http.HttpServletRequestWrapper;
+import io.jenkins.servlet.http.HttpServletResponseWrapper;
+import io.jenkins.servlet.http.HttpSessionWrapper;
+import io.jenkins.servlet.http.PartWrapper;
+import java.io.BufferedReader;
import java.io.IOException;
+import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
+import java.security.Principal;
+import java.util.ArrayList;
import java.util.Calendar;
+import java.util.Collection;
import java.util.Date;
+import java.util.Enumeration;
import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javax.servlet.AsyncContext;
+import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpUpgradeHandler;
+import javax.servlet.http.Part;
+import javax.servlet.http.PushBuilder;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.beanutils.BeanUtils;
@@ -48,7 +84,9 @@
*
* @see Stapler#getCurrentRequest()
* @author Kohsuke Kawaguchi
+ * @deprecated use {@link StaplerRequest2}
*/
+@Deprecated
public interface StaplerRequest extends HttpServletRequest {
/**
* Gets the {@link Stapler} instance that this belongs to.
@@ -553,4 +591,1329 @@ public String getUrlNames() {
* {@code makeStaplerProxy}.
*/
RenderOnDemandParameters createJavaScriptProxyParameters(Object toBeExported);
+
+ static StaplerRequest2 toStaplerRequest2(StaplerRequest from) {
+ if (from instanceof StaplerRequestWrapper javax) {
+ return javax.toStaplerRequest2();
+ }
+ return new StaplerRequest2WrapperImpl(from);
+ }
+
+ static StaplerRequest fromStaplerRequest2(StaplerRequest2 from) {
+ if (from instanceof StaplerRequest2Wrapper jakarta) {
+ return jakarta.toStaplerRequest();
+ }
+ return new StaplerRequestWrapperImpl(from);
+ }
+
+ interface StaplerRequest2Wrapper {
+ org.kohsuke.stapler.StaplerRequest toStaplerRequest();
+ }
+
+ class StaplerRequest2WrapperImpl
+ implements StaplerRequest2,
+ ServletRequestWrapper.JakartaServletRequestWrapper,
+ HttpServletRequestWrapper.JakartaHttpServletRequestWrapper,
+ StaplerRequest2Wrapper {
+ private final StaplerRequest from;
+
+ public StaplerRequest2WrapperImpl(StaplerRequest from) {
+ this.from = Objects.requireNonNull(from);
+ }
+
+ @Override
+ public Object getAttribute(String name) {
+ return from.getAttribute(name);
+ }
+
+ @Override
+ public Enumeration getAttributeNames() {
+ return from.getAttributeNames();
+ }
+
+ @Override
+ public String getCharacterEncoding() {
+ return from.getCharacterEncoding();
+ }
+
+ @Override
+ public void setCharacterEncoding(String env) throws UnsupportedEncodingException {
+ from.setCharacterEncoding(env);
+ }
+
+ @Override
+ public int getContentLength() {
+ return from.getContentLength();
+ }
+
+ @Override
+ public long getContentLengthLong() {
+ return from.getContentLengthLong();
+ }
+
+ @Override
+ public String getContentType() {
+ return from.getContentType();
+ }
+
+ @Override
+ public jakarta.servlet.ServletInputStream getInputStream() throws IOException {
+ return ServletInputStreamWrapper.toJakartaServletInputStream(from.getInputStream());
+ }
+
+ @Override
+ public String getParameter(String name) {
+ return from.getParameter(name);
+ }
+
+ @Override
+ public Enumeration getParameterNames() {
+ return from.getParameterNames();
+ }
+
+ @Override
+ public String[] getParameterValues(String name) {
+ return from.getParameterValues(name);
+ }
+
+ @Override
+ public Map getParameterMap() {
+ return from.getParameterMap();
+ }
+
+ @Override
+ public String getProtocol() {
+ return from.getProtocol();
+ }
+
+ @Override
+ public String getScheme() {
+ return from.getScheme();
+ }
+
+ @Override
+ public String getServerName() {
+ return from.getServerName();
+ }
+
+ @Override
+ public int getServerPort() {
+ return from.getServerPort();
+ }
+
+ @Override
+ public BufferedReader getReader() throws IOException {
+ return from.getReader();
+ }
+
+ @Override
+ public String getRemoteAddr() {
+ return from.getRemoteAddr();
+ }
+
+ @Override
+ public String getRemoteHost() {
+ return from.getRemoteHost();
+ }
+
+ @Override
+ public void setAttribute(String name, Object o) {
+ from.setAttribute(name, o);
+ }
+
+ @Override
+ public void removeAttribute(String name) {
+ from.removeAttribute(name);
+ }
+
+ @Override
+ public Locale getLocale() {
+ return from.getLocale();
+ }
+
+ @Override
+ public Enumeration getLocales() {
+ return from.getLocales();
+ }
+
+ @Override
+ public boolean isSecure() {
+ return from.isSecure();
+ }
+
+ @Override
+ public jakarta.servlet.RequestDispatcher getRequestDispatcher(String path) {
+ RequestDispatcher requestDispatcher = from.getRequestDispatcher(path);
+ return requestDispatcher != null
+ ? RequestDispatcherWrapper.toJakartaRequestDispatcher(requestDispatcher)
+ : null;
+ }
+
+ @Override
+ public String getRealPath(String path) {
+ return from.getRealPath(path);
+ }
+
+ @Override
+ public int getRemotePort() {
+ return from.getRemotePort();
+ }
+
+ @Override
+ public String getLocalName() {
+ return from.getLocalName();
+ }
+
+ @Override
+ public String getLocalAddr() {
+ return from.getLocalAddr();
+ }
+
+ @Override
+ public int getLocalPort() {
+ return from.getLocalPort();
+ }
+
+ @Override
+ public Stapler getStapler() {
+ return from.getStapler();
+ }
+
+ @Override
+ public WebApp getWebApp() {
+ return from.getWebApp();
+ }
+
+ @Override
+ public String getRestOfPath() {
+ return from.getRestOfPath();
+ }
+
+ @Override
+ public String getOriginalRestOfPath() {
+ return from.getOriginalRestOfPath();
+ }
+
+ @Override
+ public jakarta.servlet.ServletContext getServletContext() {
+ return ServletContextWrapper.toJakartaServletContext(from.getServletContext());
+ }
+
+ @Override
+ public String getRequestURIWithQueryString() {
+ return from.getRequestURIWithQueryString();
+ }
+
+ @Override
+ public StringBuffer getRequestURLWithQueryString() {
+ return from.getRequestURLWithQueryString();
+ }
+
+ @Override
+ public jakarta.servlet.RequestDispatcher getView(Object it, String viewName) throws IOException {
+ RequestDispatcher view = from.getView(it, viewName);
+ return view != null ? RequestDispatcherWrapper.toJakartaRequestDispatcher(view) : null;
+ }
+
+ @Override
+ public jakarta.servlet.RequestDispatcher getView(Class clazz, String viewName) throws IOException {
+ RequestDispatcher view = from.getView(clazz, viewName);
+ return view != null ? RequestDispatcherWrapper.toJakartaRequestDispatcher(view) : null;
+ }
+
+ @Override
+ public jakarta.servlet.RequestDispatcher getView(Klass> clazz, String viewName) throws IOException {
+ RequestDispatcher view = from.getView(clazz, viewName);
+ return view != null ? RequestDispatcherWrapper.toJakartaRequestDispatcher(view) : null;
+ }
+
+ @Override
+ public String getRootPath() {
+ return from.getRootPath();
+ }
+
+ @Override
+ public String getReferer() {
+ return from.getReferer();
+ }
+
+ @Override
+ public List getAncestors() {
+ return from.getAncestors();
+ }
+
+ @Override
+ public Ancestor findAncestor(Class type) {
+ return from.findAncestor(type);
+ }
+
+ @Override
+ public T findAncestorObject(Class type) {
+ return from.findAncestorObject(type);
+ }
+
+ @Override
+ public Ancestor findAncestor(Object o) {
+ return from.findAncestor(o);
+ }
+
+ @Override
+ public boolean hasParameter(String name) {
+ return from.hasParameter(name);
+ }
+
+ @Override
+ public String getOriginalRequestURI() {
+ return from.getOriginalRequestURI();
+ }
+
+ @Override
+ public boolean checkIfModified(long timestampOfResource, StaplerResponse2 rsp) {
+ return from.checkIfModified(timestampOfResource, StaplerResponse.fromStaplerResponse2(rsp));
+ }
+
+ @Override
+ public boolean checkIfModified(Date timestampOfResource, StaplerResponse2 rsp) {
+ return from.checkIfModified(timestampOfResource, StaplerResponse.fromStaplerResponse2(rsp));
+ }
+
+ @Override
+ public boolean checkIfModified(Calendar timestampOfResource, StaplerResponse2 rsp) {
+ return from.checkIfModified(timestampOfResource, StaplerResponse.fromStaplerResponse2(rsp));
+ }
+
+ @Override
+ public boolean checkIfModified(long timestampOfResource, StaplerResponse2 rsp, long expiration) {
+ return from.checkIfModified(timestampOfResource, StaplerResponse.fromStaplerResponse2(rsp));
+ }
+
+ @Override
+ public void bindParameters(Object bean) {
+ from.bindParameters(bean);
+ }
+
+ @Override
+ public void bindParameters(Object bean, String prefix) {
+ from.bindParameters(bean, prefix);
+ }
+
+ @Override
+ public List bindParametersToList(Class type, String prefix) {
+ return from.bindParametersToList(type, prefix);
+ }
+
+ @Override
+ public T bindParameters(Class type, String prefix) {
+ return from.bindParameters(type, prefix);
+ }
+
+ @Override
+ public T bindParameters(Class type, String prefix, int index) {
+ return from.bindParameters(type, prefix, index);
+ }
+
+ @Override
+ public T bindJSON(Class type, JSONObject src) {
+ return from.bindJSON(type, src);
+ }
+
+ @Override
+ public T bindJSON(Type genericType, Class erasure, Object json) {
+ return from.bindJSON(genericType, erasure, json);
+ }
+
+ @Override
+ public void bindJSON(Object bean, JSONObject src) {
+ from.bindJSON(bean, src);
+ }
+
+ @Override
+ public List bindJSONToList(Class type, Object src) {
+ return from.bindJSONToList(type, src);
+ }
+
+ @Override
+ public BindInterceptor getBindInterceptor() {
+ return from.getBindInterceptor();
+ }
+
+ @Override
+ public BindInterceptor setBindListener(BindInterceptor bindListener) {
+ return from.setBindListener(bindListener);
+ }
+
+ @Override
+ public BindInterceptor setBindInterceptpr(BindInterceptor bindListener) {
+ return from.setBindInterceptpr(bindListener);
+ }
+
+ @Override
+ public BindInterceptor setBindInterceptor(BindInterceptor bindListener) {
+ return from.setBindInterceptor(bindListener);
+ }
+
+ @Override
+ public JSONObject getSubmittedForm() throws jakarta.servlet.ServletException {
+ try {
+ return from.getSubmittedForm();
+ } catch (ServletException e) {
+ throw ServletExceptionWrapper.toJakartaServletException(e);
+ }
+ }
+
+ @Override
+ public FileItem getFileItem2(String name) throws jakarta.servlet.ServletException, IOException {
+ try {
+ return from.getFileItem2(name);
+ } catch (ServletException e) {
+ throw ServletExceptionWrapper.toJakartaServletException(e);
+ }
+ }
+
+ @Override
+ public org.apache.commons.fileupload.FileItem getFileItem(String name)
+ throws jakarta.servlet.ServletException, IOException {
+ try {
+ return from.getFileItem(name);
+ } catch (ServletException e) {
+ throw ServletExceptionWrapper.toJakartaServletException(e);
+ }
+ }
+
+ @Override
+ public boolean isJavaScriptProxyCall() {
+ return from.isJavaScriptProxyCall();
+ }
+
+ @Override
+ public BoundObjectTable getBoundObjectTable() {
+ return from.getBoundObjectTable();
+ }
+
+ @Override
+ public String createJavaScriptProxy(Object toBeExported) {
+ return from.createJavaScriptProxy(toBeExported);
+ }
+
+ @Override
+ public RenderOnDemandParameters createJavaScriptProxyParameters(Object toBeExported) {
+ StaplerRequest.RenderOnDemandParameters result = from.createJavaScriptProxyParameters(toBeExported);
+ return new RenderOnDemandParameters(result.proxyMethod, result.url, result.crumb, result.urlNames);
+ }
+
+ @Override
+ public jakarta.servlet.AsyncContext startAsync() {
+ return AsyncContextWrapper.toJakartaAsyncContext(from.startAsync());
+ }
+
+ @Override
+ public jakarta.servlet.AsyncContext startAsync(
+ jakarta.servlet.ServletRequest servletRequest, jakarta.servlet.ServletResponse servletResponse) {
+ return AsyncContextWrapper.toJakartaAsyncContext(from.startAsync(
+ ServletRequestWrapper.fromJakartaServletRequest(servletRequest),
+ ServletResponseWrapper.fromJakartaServletResponse(servletResponse)));
+ }
+
+ @Override
+ public boolean isAsyncStarted() {
+ return from.isAsyncStarted();
+ }
+
+ @Override
+ public boolean isAsyncSupported() {
+ return from.isAsyncSupported();
+ }
+
+ @Override
+ public jakarta.servlet.AsyncContext getAsyncContext() {
+ return AsyncContextWrapper.toJakartaAsyncContext(from.getAsyncContext());
+ }
+
+ @Override
+ public jakarta.servlet.DispatcherType getDispatcherType() {
+ return DispatcherTypeWrapper.toJakartaDispatcherType(from.getDispatcherType());
+ }
+
+ @Override
+ public String getAuthType() {
+ return from.getAuthType();
+ }
+
+ @Override
+ public jakarta.servlet.http.Cookie[] getCookies() {
+ Cookie[] cookies = from.getCookies();
+ if (cookies == null) {
+ return null;
+ }
+ return Stream.of(cookies)
+ .map(CookieWrapper::toJakartaServletHttpCookie)
+ .toArray(jakarta.servlet.http.Cookie[]::new);
+ }
+
+ @Override
+ public long getDateHeader(String name) {
+ return from.getDateHeader(name);
+ }
+
+ @Override
+ public String getHeader(String name) {
+ return from.getHeader(name);
+ }
+
+ @Override
+ public Enumeration getHeaders(String name) {
+ return from.getHeaders(name);
+ }
+
+ @Override
+ public Enumeration getHeaderNames() {
+ return from.getHeaderNames();
+ }
+
+ @Override
+ public int getIntHeader(String name) {
+ return from.getIntHeader(name);
+ }
+
+ @Override
+ public jakarta.servlet.http.HttpServletMapping getHttpServletMapping() {
+ return HttpServletMappingWrapper.toJakartaHttpServletMapping(from.getHttpServletMapping());
+ }
+
+ @Override
+ public String getMethod() {
+ return from.getMethod();
+ }
+
+ @Override
+ public String getPathInfo() {
+ return from.getPathInfo();
+ }
+
+ @Override
+ public String getPathTranslated() {
+ return from.getPathTranslated();
+ }
+
+ @Override
+ public jakarta.servlet.http.PushBuilder newPushBuilder() {
+ // TODO implement this
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getContextPath() {
+ return from.getContextPath();
+ }
+
+ @Override
+ public String getQueryString() {
+ return from.getQueryString();
+ }
+
+ @Override
+ public String getRemoteUser() {
+ return from.getRemoteUser();
+ }
+
+ @Override
+ public boolean isUserInRole(String role) {
+ return from.isUserInRole(role);
+ }
+
+ @Override
+ public Principal getUserPrincipal() {
+ return from.getUserPrincipal();
+ }
+
+ @Override
+ public String getRequestedSessionId() {
+ return from.getRequestedSessionId();
+ }
+
+ @Override
+ public String getRequestURI() {
+ return from.getRequestURI();
+ }
+
+ @Override
+ public StringBuffer getRequestURL() {
+ return from.getRequestURL();
+ }
+
+ @Override
+ public String getServletPath() {
+ return from.getServletPath();
+ }
+
+ @Override
+ public jakarta.servlet.http.HttpSession getSession(boolean create) {
+ HttpSession session = from.getSession(create);
+ return session != null ? HttpSessionWrapper.toJakartaHttpSession(session) : null;
+ }
+
+ @Override
+ public jakarta.servlet.http.HttpSession getSession() {
+ HttpSession session = from.getSession();
+ return session != null ? HttpSessionWrapper.toJakartaHttpSession(session) : null;
+ }
+
+ @Override
+ public String changeSessionId() {
+ return from.changeSessionId();
+ }
+
+ @Override
+ public boolean isRequestedSessionIdValid() {
+ return from.isRequestedSessionIdValid();
+ }
+
+ @Override
+ public boolean isRequestedSessionIdFromCookie() {
+ return from.isRequestedSessionIdFromCookie();
+ }
+
+ @Override
+ public boolean isRequestedSessionIdFromURL() {
+ return from.isRequestedSessionIdFromURL();
+ }
+
+ @Override
+ public boolean isRequestedSessionIdFromUrl() {
+ return from.isRequestedSessionIdFromUrl();
+ }
+
+ @Override
+ public boolean authenticate(jakarta.servlet.http.HttpServletResponse response)
+ throws IOException, jakarta.servlet.ServletException {
+ try {
+ return from.authenticate(HttpServletResponseWrapper.fromJakartaHttpServletResponse(response));
+ } catch (ServletException e) {
+ throw ServletExceptionWrapper.toJakartaServletException(e);
+ }
+ }
+
+ @Override
+ public void login(String username, String password) throws jakarta.servlet.ServletException {
+ try {
+ from.login(username, password);
+ } catch (ServletException e) {
+ throw ServletExceptionWrapper.toJakartaServletException(e);
+ }
+ }
+
+ @Override
+ public void logout() throws jakarta.servlet.ServletException {
+ try {
+ from.logout();
+ } catch (ServletException e) {
+ throw ServletExceptionWrapper.toJakartaServletException(e);
+ }
+ }
+
+ @Override
+ public Collection getParts() throws IOException, jakarta.servlet.ServletException {
+ try {
+ return from.getParts().stream()
+ .map(PartWrapper::toJakartaPart)
+ .collect(Collectors.toCollection(ArrayList::new));
+ } catch (ServletException e) {
+ throw ServletExceptionWrapper.toJakartaServletException(e);
+ }
+ }
+
+ @Override
+ public jakarta.servlet.http.Part getPart(String name) throws IOException, jakarta.servlet.ServletException {
+ try {
+ return PartWrapper.toJakartaPart(from.getPart(name));
+ } catch (ServletException e) {
+ throw ServletExceptionWrapper.toJakartaServletException(e);
+ }
+ }
+
+ @Override
+ public T upgrade(Class handlerClass) {
+ // TODO implement this
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map getTrailerFields() {
+ return from.getTrailerFields();
+ }
+
+ @Override
+ public boolean isTrailerFieldsReady() {
+ return from.isTrailerFieldsReady();
+ }
+
+ @Override
+ public ServletRequest toJavaxServletRequest() {
+ return from;
+ }
+
+ @Override
+ public HttpServletRequest toJavaxHttpServletRequest() {
+ return from;
+ }
+
+ @Override
+ public StaplerRequest toStaplerRequest() {
+ return from;
+ }
+ }
+
+ interface StaplerRequestWrapper {
+ StaplerRequest2 toStaplerRequest2();
+ }
+
+ class StaplerRequestWrapperImpl
+ implements StaplerRequest,
+ ServletRequestWrapper.JavaxServletRequestWrapper,
+ HttpServletRequestWrapper.JavaxHttpServletRequestWrapper,
+ StaplerRequestWrapper {
+ private final StaplerRequest2 from;
+
+ public StaplerRequestWrapperImpl(StaplerRequest2 from) {
+ this.from = Objects.requireNonNull(from);
+ }
+
+ @Override
+ public Object getAttribute(String name) {
+ return from.getAttribute(name);
+ }
+
+ @Override
+ public Enumeration getAttributeNames() {
+ return from.getAttributeNames();
+ }
+
+ @Override
+ public String getCharacterEncoding() {
+ return from.getCharacterEncoding();
+ }
+
+ @Override
+ public void setCharacterEncoding(String env) throws UnsupportedEncodingException {
+ from.setCharacterEncoding(env);
+ }
+
+ @Override
+ public int getContentLength() {
+ return from.getContentLength();
+ }
+
+ @Override
+ public long getContentLengthLong() {
+ return from.getContentLengthLong();
+ }
+
+ @Override
+ public String getContentType() {
+ return from.getContentType();
+ }
+
+ @Override
+ public ServletInputStream getInputStream() throws IOException {
+ return ServletInputStreamWrapper.fromJakartaServletInputStream(from.getInputStream());
+ }
+
+ @Override
+ public String getParameter(String name) {
+ return from.getParameter(name);
+ }
+
+ @Override
+ public Enumeration getParameterNames() {
+ return from.getParameterNames();
+ }
+
+ @Override
+ public String[] getParameterValues(String name) {
+ return from.getParameterValues(name);
+ }
+
+ @Override
+ public Map getParameterMap() {
+ return from.getParameterMap();
+ }
+
+ @Override
+ public String getProtocol() {
+ return from.getProtocol();
+ }
+
+ @Override
+ public String getScheme() {
+ return from.getScheme();
+ }
+
+ @Override
+ public String getServerName() {
+ return from.getServerName();
+ }
+
+ @Override
+ public int getServerPort() {
+ return from.getServerPort();
+ }
+
+ @Override
+ public BufferedReader getReader() throws IOException {
+ return from.getReader();
+ }
+
+ @Override
+ public String getRemoteAddr() {
+ return from.getRemoteAddr();
+ }
+
+ @Override
+ public String getRemoteHost() {
+ return from.getRemoteHost();
+ }
+
+ @Override
+ public void setAttribute(String name, Object o) {
+ from.setAttribute(name, o);
+ }
+
+ @Override
+ public void removeAttribute(String name) {
+ from.removeAttribute(name);
+ }
+
+ @Override
+ public Locale getLocale() {
+ return from.getLocale();
+ }
+
+ @Override
+ public Enumeration getLocales() {
+ return from.getLocales();
+ }
+
+ @Override
+ public boolean isSecure() {
+ return from.isSecure();
+ }
+
+ @Override
+ public RequestDispatcher getRequestDispatcher(String path) {
+ jakarta.servlet.RequestDispatcher requestDispatcher = from.getRequestDispatcher(path);
+ return requestDispatcher != null
+ ? RequestDispatcherWrapper.fromJakartaRequestDispatcher(requestDispatcher)
+ : null;
+ }
+
+ @Override
+ public String getRealPath(String path) {
+ return from.getRealPath(path);
+ }
+
+ @Override
+ public int getRemotePort() {
+ return from.getRemotePort();
+ }
+
+ @Override
+ public String getLocalName() {
+ return from.getLocalName();
+ }
+
+ @Override
+ public String getLocalAddr() {
+ return from.getLocalAddr();
+ }
+
+ @Override
+ public int getLocalPort() {
+ return from.getLocalPort();
+ }
+
+ @Override
+ public Stapler getStapler() {
+ return from.getStapler();
+ }
+
+ @Override
+ public WebApp getWebApp() {
+ return from.getWebApp();
+ }
+
+ @Override
+ public String getRestOfPath() {
+ return from.getRestOfPath();
+ }
+
+ @Override
+ public String getOriginalRestOfPath() {
+ return from.getOriginalRestOfPath();
+ }
+
+ @Override
+ public ServletContext getServletContext() {
+ return ServletContextWrapper.fromJakartServletContext(from.getServletContext());
+ }
+
+ @Override
+ public String getRequestURIWithQueryString() {
+ return from.getRequestURIWithQueryString();
+ }
+
+ @Override
+ public StringBuffer getRequestURLWithQueryString() {
+ return from.getRequestURLWithQueryString();
+ }
+
+ @Override
+ public RequestDispatcher getView(Object it, String viewName) throws IOException {
+ jakarta.servlet.RequestDispatcher view = from.getView(it, viewName);
+ return view != null ? RequestDispatcherWrapper.fromJakartaRequestDispatcher(view) : null;
+ }
+
+ @Override
+ public RequestDispatcher getView(Class clazz, String viewName) throws IOException {
+ jakarta.servlet.RequestDispatcher view = from.getView(clazz, viewName);
+ return view != null ? RequestDispatcherWrapper.fromJakartaRequestDispatcher(view) : null;
+ }
+
+ @Override
+ public RequestDispatcher getView(Klass> clazz, String viewName) throws IOException {
+ jakarta.servlet.RequestDispatcher view = from.getView(clazz, viewName);
+ return view != null ? RequestDispatcherWrapper.fromJakartaRequestDispatcher(view) : null;
+ }
+
+ @Override
+ public String getRootPath() {
+ return from.getRootPath();
+ }
+
+ @Override
+ public String getReferer() {
+ return from.getReferer();
+ }
+
+ @Override
+ public List getAncestors() {
+ return from.getAncestors();
+ }
+
+ @Override
+ public Ancestor findAncestor(Class type) {
+ return from.findAncestor(type);
+ }
+
+ @Override
+ public T findAncestorObject(Class type) {
+ return from.findAncestorObject(type);
+ }
+
+ @Override
+ public Ancestor findAncestor(Object o) {
+ return from.findAncestor(o);
+ }
+
+ @Override
+ public boolean hasParameter(String name) {
+ return from.hasParameter(name);
+ }
+
+ @Override
+ public String getOriginalRequestURI() {
+ return from.getOriginalRequestURI();
+ }
+
+ @Override
+ public boolean checkIfModified(long timestampOfResource, StaplerResponse rsp) {
+ return from.checkIfModified(timestampOfResource, StaplerResponse.toStaplerResponse2(rsp));
+ }
+
+ @Override
+ public boolean checkIfModified(Date timestampOfResource, StaplerResponse rsp) {
+ return from.checkIfModified(timestampOfResource, StaplerResponse.toStaplerResponse2(rsp));
+ }
+
+ @Override
+ public boolean checkIfModified(Calendar timestampOfResource, StaplerResponse rsp) {
+ return from.checkIfModified(timestampOfResource, StaplerResponse.toStaplerResponse2(rsp));
+ }
+
+ @Override
+ public boolean checkIfModified(long timestampOfResource, StaplerResponse rsp, long expiration) {
+ return from.checkIfModified(timestampOfResource, StaplerResponse.toStaplerResponse2(rsp), expiration);
+ }
+
+ @Override
+ public void bindParameters(Object bean) {
+ from.bindParameters(bean);
+ }
+
+ @Override
+ public void bindParameters(Object bean, String prefix) {
+ from.bindParameters(bean, prefix);
+ }
+
+ @Override
+ public List bindParametersToList(Class type, String prefix) {
+ return from.bindParametersToList(type, prefix);
+ }
+
+ @Override
+ public T bindParameters(Class type, String prefix) {
+ return from.bindParameters(type, prefix);
+ }
+
+ @Override
+ public T bindParameters(Class type, String prefix, int index) {
+ return from.bindParameters(type, prefix, index);
+ }
+
+ @Override
+ public T bindJSON(Class type, JSONObject src) {
+ return from.bindJSON(type, src);
+ }
+
+ @Override
+ public T bindJSON(Type genericType, Class erasure, Object json) {
+ return from.bindJSON(genericType, erasure, json);
+ }
+
+ @Override
+ public void bindJSON(Object bean, JSONObject src) {
+ from.bindJSON(bean, src);
+ }
+
+ @Override
+ public List bindJSONToList(Class type, Object src) {
+ return from.bindJSONToList(type, src);
+ }
+
+ @Override
+ public BindInterceptor getBindInterceptor() {
+ return from.getBindInterceptor();
+ }
+
+ @Override
+ public BindInterceptor setBindListener(BindInterceptor bindListener) {
+ return from.setBindListener(bindListener);
+ }
+
+ @Override
+ public BindInterceptor setBindInterceptpr(BindInterceptor bindListener) {
+ return from.setBindInterceptpr(bindListener);
+ }
+
+ @Override
+ public BindInterceptor setBindInterceptor(BindInterceptor bindListener) {
+ return from.setBindInterceptor(bindListener);
+ }
+
+ @Override
+ public JSONObject getSubmittedForm() throws ServletException {
+ try {
+ return from.getSubmittedForm();
+ } catch (jakarta.servlet.ServletException e) {
+ throw ServletExceptionWrapper.fromJakartaServletException(e);
+ }
+ }
+
+ @Override
+ public FileItem getFileItem2(String name) throws ServletException, IOException {
+ try {
+ return from.getFileItem2(name);
+ } catch (jakarta.servlet.ServletException e) {
+ throw ServletExceptionWrapper.fromJakartaServletException(e);
+ }
+ }
+
+ @Override
+ public org.apache.commons.fileupload.FileItem getFileItem(String name) throws ServletException, IOException {
+ try {
+ return from.getFileItem(name);
+ } catch (jakarta.servlet.ServletException e) {
+ throw ServletExceptionWrapper.fromJakartaServletException(e);
+ }
+ }
+
+ @Override
+ public boolean isJavaScriptProxyCall() {
+ return from.isJavaScriptProxyCall();
+ }
+
+ @Override
+ public BoundObjectTable getBoundObjectTable() {
+ return from.getBoundObjectTable();
+ }
+
+ @Override
+ public String createJavaScriptProxy(Object toBeExported) {
+ return from.createJavaScriptProxy(toBeExported);
+ }
+
+ @Override
+ public RenderOnDemandParameters createJavaScriptProxyParameters(Object toBeExported) {
+ StaplerRequest2.RenderOnDemandParameters result = from.createJavaScriptProxyParameters(toBeExported);
+ return new RenderOnDemandParameters(result.proxyMethod, result.crumb, result.url, result.urlNames);
+ }
+
+ @Override
+ public AsyncContext startAsync() {
+ return AsyncContextWrapper.fromJakartaAsyncContext(from.startAsync());
+ }
+
+ @Override
+ public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) {
+ return AsyncContextWrapper.fromJakartaAsyncContext(from.startAsync(
+ ServletRequestWrapper.toJakartaServletRequest(servletRequest),
+ ServletResponseWrapper.toJakartaServletResponse(servletResponse)));
+ }
+
+ @Override
+ public boolean isAsyncStarted() {
+ return from.isAsyncStarted();
+ }
+
+ @Override
+ public boolean isAsyncSupported() {
+ return from.isAsyncSupported();
+ }
+
+ @Override
+ public AsyncContext getAsyncContext() {
+ return AsyncContextWrapper.fromJakartaAsyncContext(from.getAsyncContext());
+ }
+
+ @Override
+ public DispatcherType getDispatcherType() {
+ return DispatcherTypeWrapper.fromJakartaDispatcherType(from.getDispatcherType());
+ }
+
+ @Override
+ public String getAuthType() {
+ return from.getAuthType();
+ }
+
+ @Override
+ public Cookie[] getCookies() {
+ jakarta.servlet.http.Cookie[] cookies = from.getCookies();
+ if (cookies == null) {
+ return null;
+ }
+ return Stream.of(cookies)
+ .map(CookieWrapper::fromJakartaServletHttpCookie)
+ .toArray(Cookie[]::new);
+ }
+
+ @Override
+ public long getDateHeader(String name) {
+ return from.getDateHeader(name);
+ }
+
+ @Override
+ public String getHeader(String name) {
+ return from.getHeader(name);
+ }
+
+ @Override
+ public Enumeration getHeaders(String name) {
+ return from.getHeaders(name);
+ }
+
+ @Override
+ public Enumeration getHeaderNames() {
+ return from.getHeaderNames();
+ }
+
+ @Override
+ public int getIntHeader(String name) {
+ return from.getIntHeader(name);
+ }
+
+ @Override
+ public HttpServletMapping getHttpServletMapping() {
+ return HttpServletMappingWrapper.fromJakartaHttpServletMapping(from.getHttpServletMapping());
+ }
+
+ @Override
+ public String getMethod() {
+ return from.getMethod();
+ }
+
+ @Override
+ public String getPathInfo() {
+ return from.getPathInfo();
+ }
+
+ @Override
+ public String getPathTranslated() {
+ return from.getPathTranslated();
+ }
+
+ @Override
+ public PushBuilder newPushBuilder() {
+ // TODO implement this
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getContextPath() {
+ return from.getContextPath();
+ }
+
+ @Override
+ public String getQueryString() {
+ return from.getQueryString();
+ }
+
+ @Override
+ public String getRemoteUser() {
+ return from.getRemoteUser();
+ }
+
+ @Override
+ public boolean isUserInRole(String role) {
+ return from.isUserInRole(role);
+ }
+
+ @Override
+ public Principal getUserPrincipal() {
+ return from.getUserPrincipal();
+ }
+
+ @Override
+ public String getRequestedSessionId() {
+ return from.getRequestedSessionId();
+ }
+
+ @Override
+ public String getRequestURI() {
+ return from.getRequestURI();
+ }
+
+ @Override
+ public StringBuffer getRequestURL() {
+ return from.getRequestURL();
+ }
+
+ @Override
+ public String getServletPath() {
+ return from.getServletPath();
+ }
+
+ @Override
+ public HttpSession getSession(boolean create) {
+ jakarta.servlet.http.HttpSession session = from.getSession(create);
+ return session != null ? HttpSessionWrapper.fromJakartaHttpSession(session) : null;
+ }
+
+ @Override
+ public HttpSession getSession() {
+ jakarta.servlet.http.HttpSession session = from.getSession();
+ return session != null ? HttpSessionWrapper.fromJakartaHttpSession(session) : null;
+ }
+
+ @Override
+ public String changeSessionId() {
+ return from.changeSessionId();
+ }
+
+ @Override
+ public boolean isRequestedSessionIdValid() {
+ return from.isRequestedSessionIdValid();
+ }
+
+ @Override
+ public boolean isRequestedSessionIdFromCookie() {
+ return from.isRequestedSessionIdFromCookie();
+ }
+
+ @Override
+ public boolean isRequestedSessionIdFromURL() {
+ return from.isRequestedSessionIdFromURL();
+ }
+
+ @Override
+ public boolean isRequestedSessionIdFromUrl() {
+ return from.isRequestedSessionIdFromUrl();
+ }
+
+ @Override
+ public boolean authenticate(HttpServletResponse response) throws IOException, ServletException {
+ try {
+ return from.authenticate(HttpServletResponseWrapper.toJakartaHttpServletResponse(response));
+ } catch (jakarta.servlet.ServletException e) {
+ throw ServletExceptionWrapper.fromJakartaServletException(e);
+ }
+ }
+
+ @Override
+ public void login(String username, String password) throws ServletException {
+ try {
+ from.login(username, password);
+ } catch (jakarta.servlet.ServletException e) {
+ throw ServletExceptionWrapper.fromJakartaServletException(e);
+ }
+ }
+
+ @Override
+ public void logout() throws ServletException {
+ try {
+ from.logout();
+ } catch (jakarta.servlet.ServletException e) {
+ throw ServletExceptionWrapper.fromJakartaServletException(e);
+ }
+ }
+
+ @Override
+ public Collection getParts() throws IOException, ServletException {
+ try {
+ return from.getParts().stream()
+ .map(PartWrapper::fromJakartaPart)
+ .collect(Collectors.toCollection(ArrayList::new));
+ } catch (jakarta.servlet.ServletException e) {
+ throw ServletExceptionWrapper.fromJakartaServletException(e);
+ }
+ }
+
+ @Override
+ public Part getPart(String name) throws IOException, ServletException {
+ try {
+ return PartWrapper.fromJakartaPart(from.getPart(name));
+ } catch (jakarta.servlet.ServletException e) {
+ throw ServletExceptionWrapper.fromJakartaServletException(e);
+ }
+ }
+
+ @Override
+ public T upgrade(Class handlerClass) {
+ // TODO implement this
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map getTrailerFields() {
+ return from.getTrailerFields();
+ }
+
+ @Override
+ public boolean isTrailerFieldsReady() {
+ return from.isTrailerFieldsReady();
+ }
+
+ @Override
+ public jakarta.servlet.ServletRequest toJakartaServletRequest() {
+ return from;
+ }
+
+ @Override
+ public jakarta.servlet.http.HttpServletRequest toJakartaHttpServletRequest() {
+ return from;
+ }
+
+ @Override
+ public StaplerRequest2 toStaplerRequest2() {
+ return from;
+ }
+ }
}
diff --git a/core/src/main/java/org/kohsuke/stapler/StaplerRequest2.java b/core/src/main/java/org/kohsuke/stapler/StaplerRequest2.java
new file mode 100644
index 000000000..91b4eb996
--- /dev/null
+++ b/core/src/main/java/org/kohsuke/stapler/StaplerRequest2.java
@@ -0,0 +1,556 @@
+/*
+ * Copyright (c) 2004-2010, Kohsuke Kawaguchi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.kohsuke.stapler;
+
+import jakarta.servlet.RequestDispatcher;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+import org.apache.commons.beanutils.BeanUtils;
+import org.apache.commons.beanutils.ConvertUtils;
+import org.apache.commons.fileupload2.core.FileItem;
+import org.kohsuke.stapler.bind.BoundObjectTable;
+import org.kohsuke.stapler.json.SubmittedForm;
+import org.kohsuke.stapler.lang.Klass;
+
+/**
+ * Defines additional parameters/operations made available by Stapler.
+ *
+ * @see Stapler#getCurrentRequest2()
+ * @author Kohsuke Kawaguchi
+ */
+public interface StaplerRequest2 extends HttpServletRequest {
+ /**
+ * Gets the {@link Stapler} instance that this belongs to.
+ */
+ Stapler getStapler();
+
+ /**
+ * Short for {@code getStapler().getWebApp()}
+ */
+ WebApp getWebApp();
+
+ /**
+ * Returns the additional URL portion that wasn't used by the stapler,
+ * excluding the query string.
+ *
+ *
+ * For example, if the requested URL is "foo/bar/zot/abc?def=ghi" and
+ * "foo/bar" portion matched {@code bar.jsp}, this method returns
+ * "/zot/abc".
+ *
+ *
+ * If this method is invoked from getters or {@link StaplerProxy#getTarget()}
+ * during the object traversal, this method returns the path portion
+ * that is not yet processed.
+ *
+ * @return
+ * can be empty string, but never null.
+ */
+ String getRestOfPath();
+
+ /**
+ * Returns the same thing as {@link #getRestOfPath()} but in the pre-decoded form,
+ * so all "%HH"s as present in the request URL is intact.
+ */
+ String getOriginalRestOfPath();
+
+ /**
+ * Returns the {@link ServletContext} object given to the stapler
+ * dispatcher servlet.
+ */
+ @Override
+ ServletContext getServletContext();
+
+ /**
+ * {@link #getRequestURI()} plus additional query string part, if it exists.
+ */
+ String getRequestURIWithQueryString();
+
+ /**
+ * {@link #getRequestURL()} plus additional query string part, if it exists.
+ */
+ StringBuffer getRequestURLWithQueryString();
+
+ /**
+ * Gets the {@link RequestDispatcher} that represents a specific view
+ * for the given object.
+ *
+ * This support both JSP and Jelly.
+ *
+ * @param viewName
+ * If this name is relative name like "foo.jsp" or "bar/zot.jelly",
+ * then the corresponding "side file" is searched by this name.
+ *
+ * For Jelly, this also accepts absolute path name that starts
+ * with '/', such as "/foo/bar/zot.jelly". In this case,
+ * {@code it.getClass().getClassLoader()} is searched for this script.
+ *
+ * @return null
+ * if neither JSP nor Jelly is not found by the given name.
+ */
+ RequestDispatcher getView(Object it, String viewName) throws IOException;
+
+ /**
+ * Convenience method to call {@link #getView(Klass, String)} with {@link Class}.
+ */
+ RequestDispatcher getView(Class clazz, String viewName) throws IOException;
+
+ /**
+ * Gets the {@link RequestDispatcher} that represents a specific view
+ * for the given class.
+ *
+ *
+ * Unlike {@link #getView(Object, String)}, calling this request dispatcher
+ * doesn't set the "it" variable, so
+ * {@code getView(it.getClass(),viewName)} and {@code getView(it,viewName)}
+ * aren't the same thing.
+ */
+ RequestDispatcher getView(Klass> clazz, String viewName) throws IOException;
+
+ /**
+ * Gets the part of the request URL from protocol up to the context path.
+ * So typically it's something like {@code http://foobar:8080/something}
+ */
+ String getRootPath();
+
+ /**
+ * Gets the referer header (like "http://foobar.com/zot") or null.
+ *
+ * This is just a convenience method.
+ */
+ String getReferer();
+
+ /**
+ * Returns a list of ancestor objects that lead to the "it" object.
+ * The returned list contains {@link Ancestor} objects sorted in the
+ * order from root to the "it" object.
+ *
+ *
+ * For example, if the URL was "foo/bar/zot" and the "it" object
+ * was determined as root.getFoo().getBar("zot")
,
+ * then this list will contain the following 3 objects in this order:
+ *
+ * - the root object
+ *
- root.getFoo() object
+ *
- root.getFoo().getBar("zot") object (the "it" object)
+ *
+ *
+ *
+ *
+ * @return
+ * list of {@link Ancestor}s. Can be empty, but always non-null.
+ */
+ List getAncestors();
+
+ /**
+ * Finds the nearest ancestor that has the object of the given type, or null if not found.
+ */
+ Ancestor findAncestor(Class type);
+
+ /**
+ * Short for {@code findAncestor(type).getObject()}, with proper handling for null de-reference.
+ * This version is also type safe.
+ */
+ T findAncestorObject(Class type);
+
+ /**
+ * Finds the nearest ancestor whose {@link Ancestor#getObject()} matches the given object.
+ */
+ Ancestor findAncestor(Object o);
+
+ /**
+ * Short for {@code getParameter(name)!=null}
+ */
+ boolean hasParameter(String name);
+
+ /**
+ * Gets the {@link HttpServletRequest#getRequestURI() request URI}
+ * of the original request, so that you can access the value even from
+ * JSP.
+ */
+ String getOriginalRequestURI();
+
+ /**
+ * Checks "If-Modified-Since" header and returns false
+ * if the resource needs to be served.
+ *
+ *
+ * This method can behave in three ways.
+ *
+ *
+ * - If {@code timestampOfResource} is 0 or negative,
+ * this method just returns false.
+ *
+ *
- If "If-Modified-Since" header is sent and if it's bigger than
+ * {@code timestampOfResource}, then this method sets
+ * {@link HttpServletResponse#SC_NOT_MODIFIED} as the response code
+ * and returns true.
+ *
+ *
- Otherwise, "Last-Modified" header is added with {@code timestampOfResource} value,
+ * and this method returns false.
+ *
+ *
+ *
+ * This method sends out the "Expires" header to force browser
+ * to re-validate all the time.
+ *
+ * @param timestampOfResource
+ * The time stamp of the resource.
+ * @param rsp
+ * This object is updated accordingly to simplify processing.
+ *
+ * @return
+ * false to indicate that the caller has to serve the actual resource.
+ * true to indicate that the caller should just quit processing right there
+ * (and send back {@link HttpServletResponse#SC_NOT_MODIFIED}.
+ */
+ boolean checkIfModified(long timestampOfResource, StaplerResponse2 rsp);
+
+ /**
+ * @see #checkIfModified(long, StaplerResponse2)
+ */
+ boolean checkIfModified(Date timestampOfResource, StaplerResponse2 rsp);
+
+ /**
+ * @see #checkIfModified(long, StaplerResponse2)
+ */
+ boolean checkIfModified(Calendar timestampOfResource, StaplerResponse2 rsp);
+
+ /**
+ * @param expiration
+ * The number of milliseconds until the resource will "expire".
+ * Until it expires the browser will be allowed to cache it
+ * and serve it without checking back with the server.
+ * After it expires, the client will send conditional GET to
+ * check if the resource is actually modified or not.
+ * If 0, it will immediately expire.
+ *
+ * @see #checkIfModified(long, StaplerResponse2)
+ */
+ boolean checkIfModified(long timestampOfResource, StaplerResponse2 rsp, long expiration);
+
+ /**
+ * Binds form parameters to a bean by using introspection.
+ *
+ * For example, if there's a parameter called 'foo' that has value 'abc',
+ * then {@code bean.setFoo('abc')} will be invoked. This will be repeated
+ * for all parameters. Parameters that do not have corresponding setters will
+ * be simply ignored.
+ *
+ *
+ * Values are converted into the right type. See {@link ConvertUtils#convert(String, Class)}.
+ *
+ * @see BeanUtils#setProperty(Object, String, Object)
+ *
+ * @param bean
+ * The object which will be filled out.
+ */
+ void bindParameters(Object bean);
+
+ /**
+ * Binds form parameters to a bean by using introspection.
+ *
+ * This method works like {@link #bindParameters(Object)}, but it performs a
+ * pre-processing on property names. Namely, only property names that start
+ * with the given prefix will be used for binding, and only the portion of the
+ * property name after the prefix is used.
+ *
+ * So for example, if the prefix is "foo.", then property name "foo.bar" with value
+ * "zot" will invoke {@code bean.setBar("zot")}.
+ *
+ *
+ * @deprecated
+ * Instead of using prefix to group object among form parameter names,
+ * use structured form submission and {@link #bindJSON(Class, JSONObject)}.
+ */
+ @Deprecated
+ void bindParameters(Object bean, String prefix);
+
+ /**
+ * Binds collection form parameters to beans by using introspection or
+ * constructor parameters injection.
+ *
+ *
+ * This method works like {@link #bindParameters(Object,String)} and
+ * {@link #bindParameters(Class, String)}, but it assumes
+ * that form parameters have multiple-values, and use individual values to
+ * fill in multiple beans.
+ *
+ *
+ * For example, if {@code getParameterValues("foo")=={"abc","def"}}
+ * and {@code getParameterValues("bar")=={"5","3"}}, then this method will
+ * return two objects (the first with "abc" and "5", the second with
+ * "def" and "3".)
+ *
+ * @param type
+ * Type of the bean to be created. This class must have the default no-arg
+ * constructor.
+ *
+ * @param prefix
+ * See {@link #bindParameters(Object, String)} for details.
+ *
+ * @return
+ * Can be empty but never null.
+ *
+ *
+ * @deprecated
+ * Instead of using prefix to group object among form parameter names,
+ * use structured form submission and {@link #bindJSON(Class, JSONObject)}.
+ */
+ @Deprecated
+ List bindParametersToList(Class type, String prefix);
+
+ /**
+ * Instantiates a new object by injecting constructor parameters from the form parameters.
+ *
+ *
+ * The given class must have a constructor annotated with '@stapler-constructor',
+ * and must be processed by the maven-stapler-plugin, so that the parameter names
+ * of the constructor is available at runtime.
+ *
+ *
+ * The prefix is used to control the form parameter name. For example,
+ * if the prefix is "foo." and if the constructor is define as
+ * Foo(String a, String b)
, then the constructor will be invoked
+ * as new Foo(getParameter("foo.a"),getParameter("foo.b"))
.
+ *
+ * @deprecated
+ * Instead of using prefix to group object among form parameter names,
+ * use structured form submission and {@link #bindJSON(Class, JSONObject)}.
+ */
+ @Deprecated
+ T bindParameters(Class type, String prefix);
+
+ /**
+ * Works like {@link #bindParameters(Class, String)} but uses n-th value
+ * of all the parameters.
+ *
+ *
+ * This is useful for creating multiple instances from repeated form fields.
+ *
+ *
+ * @deprecated
+ * Instead of using prefix to group object among form parameter names,
+ * use structured form submission and {@link #bindJSON(Class, JSONObject)}.
+ */
+ @Deprecated
+ T bindParameters(Class type, String prefix, int index);
+
+ /**
+ * Data-bind from a {@link JSONObject} to the given target type,
+ * by using introspection or constructor parameters injection.
+ *
+ *
+ * For example, if you have a constructor that looks like the following:
+ *
+ *
+ * class Foo {
+ * @{@link DataBoundConstructor}
+ * public Foo(Integer x, String y, boolean z, Bar bar) { ... }
+ * }
+ *
+ * class Bar {
+ * @{@link DataBoundConstructor}
+ * public Bar(int x, int y) {}
+ * }
+ *
+ *
+ * ... and if JSONObject looks like
+ *
+ * { y:"text", z:true, bar:{x:1,y:2}}
+ *
+ * then, this method returns
+ *
+ * new Foo(null,"text",true,new Bar(1,2))
+ *
+ * Sub-typing: In the above example,
+ * a new instance of {@code Bar} was created,
+ * but you can also create a subtype of Bar by having the '$class' property in
+ * JSON like this:
+ *
+ *
+ * class BarEx extends Bar {
+ * @{@link DataBoundConstructor}
+ * public BarEx(int a, int b, int c) {}
+ * }
+ *
+ * { y:"text", z:true, bar: { $class:"p.k.g.BarEx", a:1, b:2, c:3 } }
+ *
+ *
+ *
+ * The type that shows up in the constructor ({@code Bar} in this case)
+ * can be an interface or an abstract class.
+ */
+ T bindJSON(Class type, JSONObject src);
+
+ /**
+ * Data-bind from one of the JSON object types ({@link JSONObject}, {@link JSONArray},
+ * {@link String}, {@link Integer}, and so on) to the expected type given as an argument.
+ *
+ * @param genericType
+ * The generic type of the 'erasure' parameter.
+ * @param erasure
+ * The expected type to convert the JSON argument to.
+ * @param json
+ * One of the JSON value type.
+ */
+ T bindJSON(Type genericType, Class erasure, Object json);
+
+ /**
+ * Data-binds from {@link JSONObject} to the given object.
+ *
+ *
+ * This method is bit like {@link #bindJSON(Class, JSONObject)}, except that this method
+ * populates an existing object, instead of creating a new instance.
+ *
+ *
+ * This method is also bit like {@link #bindParameters(Object, String)}, in that it
+ * populates an existing object from a form submission, except that this method
+ * obtains data from {@link JSONObject} thus more structured, whereas {@link #bindParameters(Object, String)}
+ * uses the map structure of the form submission.
+ */
+ void bindJSON(Object bean, JSONObject src);
+
+ /**
+ * Data-bind from either {@link JSONObject} or {@link JSONArray} to a list,
+ * by using {@link #bindJSON(Class, JSONObject)} as the lower-level mechanism.
+ *
+ *
+ * If the source is {@link JSONObject}, the returned list will contain
+ * a single item. If it is {@link JSONArray}, each item will be bound.
+ * If it is null, then the list will be empty.
+ */
+ List bindJSONToList(Class type, Object src);
+
+ /**
+ * Gets the {@link BindInterceptor} set for this request.
+ *
+ * @see WebApp#bindInterceptors
+ */
+ BindInterceptor getBindInterceptor();
+
+ /**
+ * @deprecated
+ * Typo. Use {@link #setBindInterceptor(BindInterceptor)}
+ */
+ @Deprecated
+ BindInterceptor setBindListener(BindInterceptor bindListener);
+
+ /**
+ * @deprecated
+ * Typo. Use {@link #setBindInterceptor(BindInterceptor)}
+ */
+ @Deprecated
+ BindInterceptor setBindInterceptpr(BindInterceptor bindListener);
+
+ BindInterceptor setBindInterceptor(BindInterceptor bindListener);
+
+ /**
+ * Gets the content of the structured form submission.
+ *
+ * @see