getConnectorDescriptors() {
diff --git a/src/main/java/org/jvnet/hudson/test/HudsonTestCase.java b/src/main/java/org/jvnet/hudson/test/HudsonTestCase.java
index 64eba8806..4611424fa 100644
--- a/src/main/java/org/jvnet/hudson/test/HudsonTestCase.java
+++ b/src/main/java/org/jvnet/hudson/test/HudsonTestCase.java
@@ -88,6 +88,8 @@
import hudson.util.ReflectionUtils;
import hudson.util.StreamTaskListener;
import hudson.util.jna.GNUCLibrary;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletContextEvent;
import java.beans.PropertyDescriptor;
import java.io.BufferedReader;
import java.io.File;
@@ -98,6 +100,7 @@
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
@@ -124,8 +127,6 @@
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletContextEvent;
import jenkins.model.Jenkins;
import jenkins.model.JenkinsAdaptor;
import jenkins.model.JenkinsLocationConfiguration;
@@ -137,10 +138,10 @@
import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.apache.commons.beanutils.PropertyUtils;
-import org.eclipse.jetty.ee8.webapp.Configuration;
-import org.eclipse.jetty.ee8.webapp.WebAppContext;
-import org.eclipse.jetty.ee8.webapp.WebXmlConfiguration;
-import org.eclipse.jetty.ee8.websocket.server.config.JettyWebSocketServletContainerInitializer;
+import org.eclipse.jetty.ee9.webapp.Configuration;
+import org.eclipse.jetty.ee9.webapp.WebAppContext;
+import org.eclipse.jetty.ee9.webapp.WebXmlConfiguration;
+import org.eclipse.jetty.ee9.websocket.server.config.JettyWebSocketServletContainerInitializer;
import org.eclipse.jetty.http.HttpCompliance;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.UriCompliance;
@@ -352,9 +353,44 @@ protected void setUp() throws Exception {
jenkins.setCrumbIssuer(new TestCrumbIssuer());
- jenkins.servletContext.setAttribute("app", jenkins);
- jenkins.servletContext.setAttribute("version","?");
- WebAppMain.installExpressionFactory(new ServletContextEvent(jenkins.servletContext));
+ if (_isEE9Plus()) {
+ ServletContext servletContext;
+ try {
+ servletContext = (ServletContext) Jenkins.class.getDeclaredMethod("getServletContext").invoke(jenkins);
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError(e);
+ } catch (InvocationTargetException e) {
+ Throwable t = e.getCause();
+ if (t instanceof Exception) {
+ throw (Exception) t;
+ } else if (t instanceof Error) {
+ throw (Error) t;
+ } else {
+ throw e;
+ }
+ }
+ servletContext.setAttribute("app", jenkins);
+ servletContext.setAttribute("version", "?");
+ try {
+ WebAppMain.class.getDeclaredMethod("installExpressionFactory", ServletContextEvent.class).invoke(null, new ServletContextEvent(servletContext));
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError(e);
+ } catch (InvocationTargetException e) {
+ Throwable t = e.getCause();
+ if (t instanceof Exception) {
+ throw (Exception) t;
+ } else if (t instanceof Error) {
+ throw (Error) t;
+ } else {
+ throw e;
+ }
+ }
+ } else {
+ javax.servlet.ServletContext servletContext = jenkins.servletContext;
+ servletContext.setAttribute("app", jenkins);
+ servletContext.setAttribute("version", "?");
+ WebAppMain.installExpressionFactory(new javax.servlet.ServletContextEvent(servletContext));
+ }
JenkinsLocationConfiguration.get().setUrl(getURL().toString());
// set a default JDK to be the one that the harness is using.
@@ -378,6 +414,15 @@ protected void setUp() throws Exception {
setUpTimeout();
}
+ private static boolean _isEE9Plus() {
+ try {
+ Jenkins.class.getDeclaredMethod("getServletContext");
+ return true;
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
protected void setUpTimeout() {
if (timeout <= 0) {
// no timeout
@@ -403,7 +448,8 @@ public void run() {
* By default, we load updates from local proxy to avoid network traffic as much as possible.
*/
protected void configureUpdateCenter() throws Exception {
- final String updateCenterUrl = "http://localhost:"+ JavaNetReverseProxy.getInstance().localPort+"/update-center.json";
+ int localPort = _isEE9Plus() ? JavaNetReverseProxy2.getInstance().localPort : JavaNetReverseProxy.getInstance().localPort;
+ final String updateCenterUrl = "http://localhost:" + localPort + "/update-center.json";
// don't waste bandwidth talking to the update center
DownloadService.neverUpdate = true;
@@ -517,11 +563,34 @@ public String getUrlName() {
* you can override it.
*/
protected Hudson newHudson() throws Exception {
- File home = homeLoader.allocate();
- for (Runner r : recipes) {
- r.decorateHome(this,home);
+ if (_isEE9Plus()) {
+ File home = homeLoader.allocate();
+ for (Runner r : recipes) {
+ r.decorateHome(this,home);
+ }
+ try {
+ return Hudson.class
+ .getDeclaredConstructor(File.class, ServletContext.class, PluginManager.class)
+ .newInstance(home, createWebServer2(), useLocalPluginManager ? null : pluginManager);
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError(e);
+ } catch (InvocationTargetException e) {
+ Throwable t = e.getCause();
+ if (t instanceof Exception) {
+ throw (Exception) t;
+ } else if (t instanceof Error) {
+ throw (Error) t;
+ } else {
+ throw e;
+ }
+ }
+ } else {
+ File home = homeLoader.allocate();
+ for (Runner r : recipes) {
+ r.decorateHome(this,home);
+ }
+ return new Hudson(home, createWebServer(), useLocalPluginManager ? null : pluginManager);
}
- return new Hudson(home, createWebServer(), useLocalPluginManager ? null : pluginManager);
}
/**
@@ -542,7 +611,7 @@ public void setPluginManager(PluginManager pluginManager) {
* Prepares a webapp hosting environment to get {@link ServletContext} implementation
* that we need for testing.
*/
- protected ServletContext createWebServer() throws Exception {
+ protected ServletContext createWebServer2() throws Exception {
QueuedThreadPool qtp = new QueuedThreadPool();
qtp.setName("Jetty (HudsonTestCase)");
server = new Server(qtp);
@@ -558,7 +627,7 @@ protected ClassLoader configureClassLoader(ClassLoader loader) {
context.setResourceBase(explodedWarDir.getPath());
context.setClassLoader(getClass().getClassLoader());
context.setConfigurations(new Configuration[]{new WebXmlConfiguration()});
- context.addBean(new NoListenerConfiguration(context));
+ context.addBean(new NoListenerConfiguration2(context));
context.setServer(server);
server.setHandler(context);
JettyWebSocketServletContainerInitializer.configure(context, null);
@@ -581,6 +650,52 @@ protected ClassLoader configureClassLoader(ClassLoader loader) {
return context.getServletContext();
}
+ /**
+ * Prepares a webapp hosting environment to get {@link javax.servlet.ServletContext} implementation
+ * that we need for testing.
+ *
+ * @deprecated use {@link #createWebServer2()}
+ */
+ @Deprecated
+ protected javax.servlet.ServletContext createWebServer() throws Exception {
+ QueuedThreadPool qtp = new QueuedThreadPool();
+ qtp.setName("Jetty (HudsonTestCase)");
+ server = new Server(qtp);
+
+ explodedWarDir = WarExploder.getExplodedDir();
+ org.eclipse.jetty.ee8.webapp.WebAppContext context = new org.eclipse.jetty.ee8.webapp.WebAppContext(explodedWarDir.getPath(), contextPath) {
+ @Override
+ protected ClassLoader configureClassLoader(ClassLoader loader) {
+ // Use flat classpath in tests
+ return loader;
+ }
+ };
+ context.setResourceBase(explodedWarDir.getPath());
+ context.setClassLoader(getClass().getClassLoader());
+ context.setConfigurations(new org.eclipse.jetty.ee8.webapp.Configuration[]{new org.eclipse.jetty.ee8.webapp.WebXmlConfiguration()});
+ context.addBean(new NoListenerConfiguration(context));
+ context.setServer(server);
+ server.setHandler(context);
+ org.eclipse.jetty.ee8.websocket.server.config.JettyWebSocketServletContainerInitializer.configure(context, null);
+ context.getSecurityHandler().setLoginService(configureUserRealm());
+
+ ServerConnector connector = new ServerConnector(server);
+
+ HttpConfiguration config = connector.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration();
+ // use a bigger buffer as Stapler traces can get pretty large on deeply nested URL
+ config.setRequestHeaderSize(12 * 1024);
+ config.setHttpCompliance(HttpCompliance.RFC7230);
+ config.setUriCompliance(UriCompliance.LEGACY);
+ connector.setHost("localhost");
+
+ server.addConnector(connector);
+ server.start();
+
+ localPort = connector.getLocalPort();
+
+ return context.getServletContext();
+ }
+
/**
* Configures a security realm for a test.
*/
diff --git a/src/main/java/org/jvnet/hudson/test/JavaNetReverseProxy.java b/src/main/java/org/jvnet/hudson/test/JavaNetReverseProxy.java
index ceab85e34..34b18f353 100644
--- a/src/main/java/org/jvnet/hudson/test/JavaNetReverseProxy.java
+++ b/src/main/java/org/jvnet/hudson/test/JavaNetReverseProxy.java
@@ -24,7 +24,9 @@
* The contents are cached locally.
*
* @author Kohsuke Kawaguchi
+ * @deprecated use {@link JavaNetReverseProxy2}
*/
+@Deprecated
public class JavaNetReverseProxy extends HttpServlet {
private final Server server;
public final int localPort;
diff --git a/src/main/java/org/jvnet/hudson/test/JavaNetReverseProxy2.java b/src/main/java/org/jvnet/hudson/test/JavaNetReverseProxy2.java
new file mode 100644
index 000000000..471167d64
--- /dev/null
+++ b/src/main/java/org/jvnet/hudson/test/JavaNetReverseProxy2.java
@@ -0,0 +1,98 @@
+package org.jvnet.hudson.test;
+
+import hudson.Util;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Files;
+import org.apache.commons.io.FileUtils;
+import org.eclipse.jetty.ee9.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee9.servlet.ServletHolder;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.ContextHandlerCollection;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+
+/**
+ * Acts as a reverse proxy, so that during a test we can avoid hitting updates.jenkins.io.
+ *
+ *
+ * The contents are cached locally.
+ *
+ * @author Kohsuke Kawaguchi
+ */
+public class JavaNetReverseProxy2 extends HttpServlet {
+ private final Server server;
+ public final int localPort;
+
+ private final File cacheFolder;
+
+ public JavaNetReverseProxy2(File cacheFolder) throws Exception {
+ this.cacheFolder = cacheFolder;
+ cacheFolder.mkdirs();
+ QueuedThreadPool qtp = new QueuedThreadPool();
+ qtp.setName("Jetty (JavaNetReverseProxy)");
+ server = new Server(qtp);
+
+ ContextHandlerCollection contexts = new ContextHandlerCollection();
+ server.setHandler(contexts);
+
+ ServletContextHandler root = new ServletContextHandler(contexts, "/", ServletContextHandler.SESSIONS);
+ root.addServlet(new ServletHolder(this), "/");
+
+ ServerConnector connector = new ServerConnector(server);
+ server.addConnector(connector);
+ server.start();
+
+ localPort = connector.getLocalPort();
+ }
+
+ public void stop() throws Exception {
+ server.stop();
+ }
+
+ @Override
+ protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ String path = req.getServletPath();
+ String d = Util.getDigestOf(path);
+
+ File cache = new File(cacheFolder, d);
+ synchronized(this) {
+ if (!cache.exists()) {
+ URL url = new URL("https://updates.jenkins.io/" + path);
+ FileUtils.copyURLToFile(url,cache);
+ }
+ }
+
+ resp.setContentType(getMimeType(path));
+ Files.copy(cache.toPath(), resp.getOutputStream());
+ }
+
+ private String getMimeType(String path) {
+ int idx = path.indexOf('?');
+ if (idx >= 0) {
+ path = path.substring(0,idx);
+ }
+ if (path.endsWith(".json")) {
+ return "text/javascript";
+ }
+ return getServletContext().getMimeType(path);
+ }
+
+ private static volatile JavaNetReverseProxy2 INSTANCE;
+
+ /**
+ * Gets the default instance.
+ */
+ public static synchronized JavaNetReverseProxy2 getInstance() throws Exception {
+ if (INSTANCE == null) {
+ // TODO: think of a better location --- ideally inside the target/ dir so that clean would wipe them out
+ INSTANCE = new JavaNetReverseProxy2(new File(new File(System.getProperty("java.io.tmpdir")),"jenkins.io-cache2"));
+ }
+ return INSTANCE;
+ }
+}
diff --git a/src/main/java/org/jvnet/hudson/test/JenkinsComputerConnectorTester.java b/src/main/java/org/jvnet/hudson/test/JenkinsComputerConnectorTester.java
index 54c3604f8..efce5f309 100644
--- a/src/main/java/org/jvnet/hudson/test/JenkinsComputerConnectorTester.java
+++ b/src/main/java/org/jvnet/hudson/test/JenkinsComputerConnectorTester.java
@@ -28,9 +28,8 @@
import hudson.model.Descriptor;
import hudson.slaves.ComputerConnector;
import hudson.slaves.ComputerConnectorDescriptor;
-import java.io.IOException;
import java.util.List;
-import javax.servlet.ServletException;
+import net.sf.json.JSONObject;
import org.kohsuke.stapler.StaplerRequest;
/**
@@ -48,8 +47,14 @@ public JenkinsComputerConnectorTester(JenkinsRule testCase) {
this.jenkinsRule = testCase;
}
- public void doConfigSubmit(StaplerRequest req) throws IOException, ServletException {
- connector = req.bindJSON(ComputerConnector.class, req.getSubmittedForm().getJSONObject("connector"));
+ public void doConfigSubmit(StaplerRequest req) {
+ JSONObject form;
+ try {
+ form = req.getSubmittedForm();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ connector = req.bindJSON(ComputerConnector.class, form.getJSONObject("connector"));
}
public List getConnectorDescriptors() {
diff --git a/src/main/java/org/jvnet/hudson/test/JenkinsRule.java b/src/main/java/org/jvnet/hudson/test/JenkinsRule.java
index 4dabd43d1..f643665d0 100644
--- a/src/main/java/org/jvnet/hudson/test/JenkinsRule.java
+++ b/src/main/java/org/jvnet/hudson/test/JenkinsRule.java
@@ -98,6 +98,8 @@
import hudson.util.ReflectionUtils;
import hudson.util.StreamTaskListener;
import hudson.util.jna.GNUCLibrary;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletContextEvent;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.BufferedReader;
@@ -160,8 +162,6 @@
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletContextEvent;
import jenkins.model.Jenkins;
import jenkins.model.JenkinsAdaptor;
import jenkins.model.JenkinsLocationConfiguration;
@@ -170,13 +170,12 @@
import jenkins.security.MasterToSlaveCallable;
import net.sf.json.JSON;
import net.sf.json.JSONObject;
-import org.acegisecurity.GrantedAuthorityImpl;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.io.FileUtils;
-import org.eclipse.jetty.ee8.webapp.Configuration;
-import org.eclipse.jetty.ee8.webapp.WebAppContext;
-import org.eclipse.jetty.ee8.webapp.WebXmlConfiguration;
-import org.eclipse.jetty.ee8.websocket.server.config.JettyWebSocketServletContainerInitializer;
+import org.eclipse.jetty.ee9.webapp.Configuration;
+import org.eclipse.jetty.ee9.webapp.WebAppContext;
+import org.eclipse.jetty.ee9.webapp.WebXmlConfiguration;
+import org.eclipse.jetty.ee9.websocket.server.config.JettyWebSocketServletContainerInitializer;
import org.eclipse.jetty.http.HttpCompliance;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.UriCompliance;
@@ -246,7 +245,6 @@
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
-import org.springframework.dao.DataAccessException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
@@ -432,6 +430,15 @@ public void before() throws Throwable {
JenkinsLocationConfiguration.get().setUrl(getURL().toString());
}
+ private static boolean _isEE9Plus() {
+ try {
+ Jenkins.class.getDeclaredMethod("getServletContext");
+ return true;
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
/**
* Configures a Jenkins instance for test.
*
@@ -441,9 +448,44 @@ public void before() throws Throwable {
*/
public static void _configureJenkinsForTest(Jenkins jenkins) throws Exception {
jenkins.setNoUsageStatistics(true); // collecting usage stats from tests is pointless.
- jenkins.servletContext.setAttribute("app", jenkins);
- jenkins.servletContext.setAttribute("version", "?");
- WebAppMain.installExpressionFactory(new ServletContextEvent(jenkins.servletContext));
+ if (_isEE9Plus()) {
+ ServletContext servletContext;
+ try {
+ servletContext = (ServletContext) Jenkins.class.getDeclaredMethod("getServletContext").invoke(jenkins);
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError(e);
+ } catch (InvocationTargetException e) {
+ Throwable t = e.getCause();
+ if (t instanceof Exception) {
+ throw (Exception) t;
+ } else if (t instanceof Error) {
+ throw (Error) t;
+ } else {
+ throw e;
+ }
+ }
+ servletContext.setAttribute("app", jenkins);
+ servletContext.setAttribute("version", "?");
+ try {
+ WebAppMain.class.getDeclaredMethod("installExpressionFactory", ServletContextEvent.class).invoke(null, new ServletContextEvent(servletContext));
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError(e);
+ } catch (InvocationTargetException e) {
+ Throwable t = e.getCause();
+ if (t instanceof Exception) {
+ throw (Exception) t;
+ } else if (t instanceof Error) {
+ throw (Error) t;
+ } else {
+ throw e;
+ }
+ }
+ } else {
+ javax.servlet.ServletContext servletContext = jenkins.servletContext;
+ servletContext.setAttribute("app", jenkins);
+ servletContext.setAttribute("version", "?");
+ WebAppMain.installExpressionFactory(new javax.servlet.ServletContextEvent(servletContext));
+ }
// set a default JDK to be the one that the harness is using.
jenkins.getJDKs().add(new JDK("default", System.getProperty("java.home")));
@@ -474,7 +516,8 @@ public static void _configureUpdateCenter(Jenkins jenkins) throws Exception {
final String updateCenterUrl;
jettyLevel(Level.WARNING);
try {
- updateCenterUrl = "http://localhost:"+ JavaNetReverseProxy.getInstance().localPort+"/update-center.json";
+ int localPort = _isEE9Plus() ? JavaNetReverseProxy2.getInstance().localPort : JavaNetReverseProxy.getInstance().localPort;
+ updateCenterUrl = "http://localhost:" + localPort + "/update-center.json";
} finally {
jettyLevel(Level.INFO);
}
@@ -737,17 +780,45 @@ public String getUrlName() {
*/
protected Hudson newHudson() throws Exception {
jettyLevel(Level.WARNING);
- ServletContext webServer = createWebServer();
- File home = homeLoader.allocate();
- for (JenkinsRecipe.Runner r : recipes) {
- r.decorateHome(this,home);
- }
- try {
- return new Hudson(home, webServer, getPluginManager());
- } catch (InterruptedException x) {
- throw new AssumptionViolatedException("Jenkins startup interrupted", x);
- } finally {
- jettyLevel(Level.INFO);
+ if (_isEE9Plus()) {
+ ServletContext webServer = createWebServer2();
+ File home = homeLoader.allocate();
+ for (JenkinsRecipe.Runner r : recipes) {
+ r.decorateHome(this, home);
+ }
+ try {
+ return Hudson.class
+ .getDeclaredConstructor(File.class, ServletContext.class, PluginManager.class)
+ .newInstance(home, webServer, getPluginManager());
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError(e);
+ } catch (InvocationTargetException e) {
+ Throwable t = e.getCause();
+ if (t instanceof InterruptedException) {
+ throw new AssumptionViolatedException("Jenkins startup interrupted", e.getCause());
+ } else if (t instanceof Exception) {
+ throw (Exception) t;
+ } else if (t instanceof Error) {
+ throw (Error) t;
+ } else {
+ throw e;
+ }
+ } finally {
+ jettyLevel(Level.INFO);
+ }
+ } else {
+ javax.servlet.ServletContext webServer = createWebServer();
+ File home = homeLoader.allocate();
+ for (JenkinsRecipe.Runner r : recipes) {
+ r.decorateHome(this, home);
+ }
+ try {
+ return new Hudson(home, webServer, getPluginManager());
+ } catch (InterruptedException e) {
+ throw new AssumptionViolatedException("Jenkins startup interrupted", e);
+ } finally {
+ jettyLevel(Level.INFO);
+ }
}
}
@@ -783,23 +854,23 @@ public File getWebAppRoot() throws Exception {
}
/**
- * Prepares a webapp hosting environment to get {@link javax.servlet.ServletContext} implementation
+ * Prepares a webapp hosting environment to get {@link jakarta.servlet.ServletContext} implementation
* that we need for testing.
*/
- protected ServletContext createWebServer() throws Exception {
- return createWebServer(null);
+ protected ServletContext createWebServer2() throws Exception {
+ return createWebServer2(null);
}
/**
- * Prepares a webapp hosting environment to get {@link javax.servlet.ServletContext} implementation
+ * Prepares a webapp hosting environment to get {@link jakarta.servlet.ServletContext} implementation
* that we need for testing.
*
* @param contextAndServerConsumer configures the {@link WebAppContext} and the {@link Server} for the instance, before they are started
* @since 2.63
*/
- protected ServletContext createWebServer(@CheckForNull BiConsumer contextAndServerConsumer)
+ protected ServletContext createWebServer2(@CheckForNull BiConsumer contextAndServerConsumer)
throws Exception {
- WebAppContext context = _createWebAppContext(
+ WebAppContext context = _createWebAppContext2(
contextPath,
(x) -> localPort = x,
getClass().getClassLoader(),
@@ -822,14 +893,14 @@ protected ServletContext createWebServer(@CheckForNull BiConsumer portSetter,
ClassLoader classLoader,
int localPort,
Supplier loginServiceSupplier)
throws Exception {
- return _createWebAppContext(contextPath, portSetter, classLoader, localPort, loginServiceSupplier, null);
+ return _createWebAppContext2(contextPath, portSetter, classLoader, localPort, loginServiceSupplier, null);
}
/**
* Creates a web server on which Jenkins can run
@@ -843,7 +914,7 @@ public static WebAppContext _createWebAppContext(
* @return the {@link Server}
* @since 2.50
*/
- public static WebAppContext _createWebAppContext(
+ public static WebAppContext _createWebAppContext2(
String contextPath,
Consumer portSetter,
ClassLoader classLoader,
@@ -864,7 +935,7 @@ protected ClassLoader configureClassLoader(ClassLoader loader) {
};
context.setClassLoader(classLoader);
context.setConfigurations(new Configuration[]{new WebXmlConfiguration()});
- context.addBean(new NoListenerConfiguration(context));
+ context.addBean(new NoListenerConfiguration2(context));
context.setServer(server);
server.setHandler(context);
JettyWebSocketServletContainerInitializer.configure(context, null);
@@ -895,6 +966,130 @@ protected ClassLoader configureClassLoader(ClassLoader loader) {
return context;
}
+ /**
+ * Prepares a webapp hosting environment to get {@link javax.servlet.ServletContext} implementation
+ * that we need for testing.
+ *
+ * @deprecated {use {@link #createWebServer2()}}
+ */
+ @Deprecated
+ protected javax.servlet.ServletContext createWebServer() throws Exception {
+ return createWebServer(null);
+ }
+
+ /**
+ * Prepares a webapp hosting environment to get {@link javax.servlet.ServletContext} implementation
+ * that we need for testing.
+ *
+ * @deprecated use {@link #createWebServer2(BiConsumer)}
+ * @param contextAndServerConsumer configures the {@link org.eclipse.jetty.ee8.webapp.WebAppContext} and the {@link Server} for the instance, before they are started
+ * @since 2.63
+ */
+ @Deprecated
+ protected javax.servlet.ServletContext createWebServer(
+ @CheckForNull BiConsumer contextAndServerConsumer)
+ throws Exception {
+ org.eclipse.jetty.ee8.webapp.WebAppContext context = _createWebAppContext(
+ contextPath,
+ (x) -> localPort = x,
+ getClass().getClassLoader(),
+ localPort,
+ this::configureUserRealm,
+ contextAndServerConsumer);
+ server = context.getServer();
+ LOGGER.log(Level.INFO, "Running on {0}", getURL());
+ return context.getServletContext();
+ }
+
+ /**
+ * Creates a web server on which Jenkins can run
+ *
+ * @param contextPath the context path at which to put Jenkins
+ * @param portSetter the port on which the server runs will be set using this function
+ * @param classLoader the class loader for the {@link org.eclipse.jetty.ee8.webapp.WebAppContext}
+ * @param localPort port on which the server runs
+ * @param loginServiceSupplier configures the {@link LoginService} for the instance
+ * @return the {@link Server}
+ * @deprecated use {@link #_createWebAppContext2(String, Consumer, ClassLoader, int, Supplier)}
+ * @since 2.50
+ */
+ @Deprecated
+ public static org.eclipse.jetty.ee8.webapp.WebAppContext _createWebAppContext(
+ String contextPath,
+ Consumer portSetter,
+ ClassLoader classLoader,
+ int localPort,
+ Supplier loginServiceSupplier)
+ throws Exception {
+ return _createWebAppContext(contextPath, portSetter, classLoader, localPort, loginServiceSupplier, null);
+ }
+
+ /**
+ * Creates a web server on which Jenkins can run
+ *
+ * @param contextPath the context path at which to put Jenkins
+ * @param portSetter the port on which the server runs will be set using this function
+ * @param classLoader the class loader for the {@link org.eclipse.jetty.ee8.webapp.WebAppContext}
+ * @param localPort port on which the server runs
+ * @param loginServiceSupplier configures the {@link LoginService} for the instance
+ * @param contextAndServerConsumer configures the {@link org.eclipse.jetty.ee8.webapp.WebAppContext} and the {@link Server} for the instance, before they are started
+ * @return the {@link Server}
+ * @deprecated use {@link #_createWebAppContext2(String, Consumer, ClassLoader, int, Supplier, BiConsumer)}
+ * @since 2.50
+ */
+ @Deprecated
+ public static org.eclipse.jetty.ee8.webapp.WebAppContext _createWebAppContext(
+ String contextPath,
+ Consumer portSetter,
+ ClassLoader classLoader,
+ int localPort,
+ Supplier loginServiceSupplier,
+ @CheckForNull BiConsumer contextAndServerConsumer)
+ throws Exception {
+ QueuedThreadPool qtp = new QueuedThreadPool();
+ qtp.setName("Jetty (JenkinsRule)");
+ Server server = new Server(qtp);
+
+ org.eclipse.jetty.ee8.webapp.WebAppContext context = new org.eclipse.jetty.ee8.webapp.WebAppContext(WarExploder.getExplodedDir().getPath(), contextPath) {
+ @Override
+ protected ClassLoader configureClassLoader(ClassLoader loader) {
+ // Use flat classpath in tests
+ return loader;
+ }
+ };
+ context.setClassLoader(classLoader);
+ context.setConfigurations(new org.eclipse.jetty.ee8.webapp.Configuration[]{new org.eclipse.jetty.ee8.webapp.WebXmlConfiguration()});
+ context.addBean(new NoListenerConfiguration(context));
+ context.setServer(server);
+ server.setHandler(context);
+ org.eclipse.jetty.ee8.websocket.server.config.JettyWebSocketServletContainerInitializer.configure(context, null);
+ context.getSecurityHandler().setLoginService(loginServiceSupplier.get());
+ context.setResourceBase(WarExploder.getExplodedDir().getPath());
+
+ ServerConnector connector = new ServerConnector(server);
+ HttpConfiguration config = connector.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration();
+ // use a bigger buffer as Stapler traces can get pretty large on deeply nested URL
+ config.setRequestHeaderSize(12 * 1024);
+ config.setHttpCompliance(HttpCompliance.RFC7230);
+ config.setUriCompliance(UriCompliance.LEGACY);
+ connector.setHost("localhost");
+ if (System.getProperty("port") != null) {
+ connector.setPort(Integer.parseInt(System.getProperty("port")));
+ } else if (localPort != 0) {
+ connector.setPort(localPort);
+ }
+
+ server.addConnector(connector);
+ if (contextAndServerConsumer != null) {
+ contextAndServerConsumer.accept(context, server);
+ }
+ server.start();
+
+ portSetter.accept(connector.getLocalPort());
+
+ return context;
+ }
+
/**
* Configures a security realm for a test.
*/
diff --git a/src/main/java/org/jvnet/hudson/test/MockFolder.java b/src/main/java/org/jvnet/hudson/test/MockFolder.java
index 33bc9c3b3..86affdd0b 100644
--- a/src/main/java/org/jvnet/hudson/test/MockFolder.java
+++ b/src/main/java/org/jvnet/hudson/test/MockFolder.java
@@ -53,7 +53,6 @@
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
-import javax.servlet.ServletException;
import jenkins.model.DirectlyModifiableTopLevelItemGroup;
import jenkins.model.Jenkins;
import org.kohsuke.stapler.StaplerFallback;
@@ -162,8 +161,14 @@ public T createProject(@NonNull Class type, @NonNull
return type.cast(createProject((TopLevelItemDescriptor) Jenkins.get().getDescriptorOrDie(type), name, true));
}
- @Override public TopLevelItem doCreateItem(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
- return mixin().createTopLevelItem(req, rsp);
+ @Override public TopLevelItem doCreateItem(StaplerRequest req, StaplerResponse rsp) throws IOException {
+ try {
+ return mixin().createTopLevelItem(req, rsp);
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
@Override public String getUrlChildPrefix() {
diff --git a/src/main/java/org/jvnet/hudson/test/NoListenerConfiguration.java b/src/main/java/org/jvnet/hudson/test/NoListenerConfiguration.java
index 2b6f7c9d3..0a58b21f2 100644
--- a/src/main/java/org/jvnet/hudson/test/NoListenerConfiguration.java
+++ b/src/main/java/org/jvnet/hudson/test/NoListenerConfiguration.java
@@ -37,7 +37,9 @@
* with the home directory of our choice.
*
* @author Kohsuke Kawaguchi
+ * @deprecated use {@link NoListenerConfiguration2}
*/
+@Deprecated
public class NoListenerConfiguration extends AbstractLifeCycle {
private final WebAppContext context;
diff --git a/src/main/java/org/jvnet/hudson/test/NoListenerConfiguration2.java b/src/main/java/org/jvnet/hudson/test/NoListenerConfiguration2.java
new file mode 100644
index 000000000..366176d16
--- /dev/null
+++ b/src/main/java/org/jvnet/hudson/test/NoListenerConfiguration2.java
@@ -0,0 +1,56 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package org.jvnet.hudson.test;
+
+import hudson.WebAppMain;
+import jakarta.servlet.ServletContextListener;
+import java.util.EventListener;
+import org.eclipse.jetty.ee9.webapp.WebAppContext;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+
+/**
+ * Kills off the {@link WebAppMain} {@link ServletContextListener}.
+ *
+ *
+ * This is so that the harness can create the {@link jenkins.model.Jenkins} object.
+ * with the home directory of our choice.
+ *
+ * @author Kohsuke Kawaguchi
+ */
+public class NoListenerConfiguration2 extends AbstractLifeCycle {
+ private final WebAppContext context;
+
+ public NoListenerConfiguration2(WebAppContext context) {
+ this.context = context;
+ }
+
+ @Override
+ protected void doStart() {
+ for (EventListener eventListener : context.getEventListeners()) {
+ if (eventListener instanceof WebAppMain) {
+ context.removeEventListener(eventListener);
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/jvnet/hudson/test/RealJenkinsRule.java b/src/main/java/org/jvnet/hudson/test/RealJenkinsRule.java
index e30b2f0dc..4749edee2 100644
--- a/src/main/java/org/jvnet/hudson/test/RealJenkinsRule.java
+++ b/src/main/java/org/jvnet/hudson/test/RealJenkinsRule.java
@@ -1251,8 +1251,10 @@ public void doStatus(@QueryParameter String token) {
private static final ExecutorService STEP_RUNNER = Executors.newSingleThreadExecutor(
new NamingThreadFactory(Executors.defaultThreadFactory(), RealJenkinsRule.class.getName() + ".STEP_RUNNER"));
@POST
+ @SuppressFBWarnings(value = "BC_VACUOUS_INSTANCEOF", justification = "TODO needs triage")
public void doStep(StaplerRequest req, StaplerResponse rsp) throws Throwable {
- InputPayload input = (InputPayload) Init2.readSer(req.getInputStream(), Endpoint.class.getClassLoader());
+ InputStream is = req instanceof jakarta.servlet.ServletRequest ? ((jakarta.servlet.ServletRequest) req).getInputStream() : ((javax.servlet.ServletRequest) req).getInputStream();
+ InputPayload input = (InputPayload) Init2.readSer(is, Endpoint.class.getClassLoader());
checkToken(input.token);
Step2> s = input.step;
URL url = input.url;
@@ -1273,7 +1275,8 @@ public void doStep(StaplerRequest req, StaplerResponse rsp) throws Throwable {
} catch (CancellationException | InterruptedException e) {
err = e;
}
- Init2.writeSer(rsp.getOutputStream(), new OutputPayload(object, err));
+ OutputStream os = rsp instanceof jakarta.servlet.ServletResponse ? ((jakarta.servlet.ServletResponse) rsp).getOutputStream() : ((javax.servlet.ServletResponse) rsp).getOutputStream();
+ Init2.writeSer(os, new OutputPayload(object, err));
}
public HttpResponse doExit(@QueryParameter String token) throws IOException {
checkToken(token);
diff --git a/src/spotbugs/excludesFilter.xml b/src/spotbugs/excludesFilter.xml
index 9e97602fd..831b3e6d4 100644
--- a/src/spotbugs/excludesFilter.xml
+++ b/src/spotbugs/excludesFilter.xml
@@ -183,6 +183,7 @@
+
@@ -213,6 +214,7 @@
+
@@ -231,6 +233,7 @@
+