Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBob Foster2015-06-10 16:58:41 -0400
committerBob Foster2015-09-18 14:28:07 -0400
commit58ccad0b8944ebebfe1883aebbb8f918e799b63d (patch)
treea818d379f1ebfb34ac9d5e30dee2242e4b1b2c58
parentd16afd5aeaa14feea23ce75b6fa3a1cca63c00fa (diff)
downloadorg.eclipse.hudson.core-58ccad0b8944ebebfe1883aebbb8f918e799b63d.tar.gz
org.eclipse.hudson.core-58ccad0b8944ebebfe1883aebbb8f918e799b63d.tar.xz
org.eclipse.hudson.core-58ccad0b8944ebebfe1883aebbb8f918e799b63d.zip
Initial checkin for memory leak
-rw-r--r--hudson-core/pom.xml17
-rw-r--r--hudson-core/src/main/java/hudson/cli/CLICommand.java5
-rw-r--r--hudson-core/src/main/java/hudson/model/DependencyGraph.java6
-rw-r--r--hudson-core/src/main/java/hudson/model/Job.java5
-rw-r--r--hudson-core/src/main/java/hudson/model/Run.java4
-rw-r--r--hudson-core/src/main/java/hudson/security/HudsonFilter.java2
-rw-r--r--hudson-core/src/main/java/hudson/util/ThreadLocalUtils.java103
-rw-r--r--hudson-core/src/main/java/hudson/util/TrackedDaemonExecutor.java33
-rw-r--r--hudson-core/src/main/java/hudson/util/TrackedExecutor.java144
-rw-r--r--hudson-core/src/main/java/org/eclipse/hudson/HudsonServletContextListener.java77
-rw-r--r--hudson-core/src/main/java/org/eclipse/hudson/init/InitialSetup.java14
-rw-r--r--pom.xml7
12 files changed, 353 insertions, 64 deletions
diff --git a/hudson-core/pom.xml b/hudson-core/pom.xml
index b824f677..d985822f 100644
--- a/hudson-core/pom.xml
+++ b/hudson-core/pom.xml
@@ -786,6 +786,19 @@
</dependency>
<dependency>
+ <groupId>org.sonatype.sisu</groupId>
+ <artifactId>sisu-guice</artifactId>
+ <version>${sisuGuice.version}</version>
+ <classifier>no_aop</classifier>
+ <exclusions>
+ <exclusion>
+ <groupId>org.sonatype.sisu</groupId>
+ <artifactId>sisu-guava</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.1</version>
@@ -805,6 +818,10 @@
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
</exclusion>
+ <exclusion>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </exclusion>
</exclusions>
</dependency>
</dependencies>
diff --git a/hudson-core/src/main/java/hudson/cli/CLICommand.java b/hudson-core/src/main/java/hudson/cli/CLICommand.java
index 10f696df..e8ea4ec8 100644
--- a/hudson-core/src/main/java/hudson/cli/CLICommand.java
+++ b/hudson-core/src/main/java/hudson/cli/CLICommand.java
@@ -396,4 +396,9 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
public static CLICommand getCurrent() {
return CURRENT_COMMAND.get();
}
+
+ /*package*/ static void removeCurrent() {
+ CURRENT_COMMAND.remove();
+ }
+
}
diff --git a/hudson-core/src/main/java/hudson/model/DependencyGraph.java b/hudson-core/src/main/java/hudson/model/DependencyGraph.java
index 4f8b08db..ff7c6bbc 100644
--- a/hudson-core/src/main/java/hudson/model/DependencyGraph.java
+++ b/hudson-core/src/main/java/hudson/model/DependencyGraph.java
@@ -86,7 +86,11 @@ public final class DependencyGraph implements Comparator<AbstractProject> {
built = true;
alreadyComputedProjects.clear();
} finally {
- SecurityContextHolder.setContext(saveCtx);
+ if (saveCtx.getAuthentication() == null) {
+ SecurityContextHolder.clearContext();
+ } else {
+ SecurityContextHolder.setContext(saveCtx);
+ }
}
}
diff --git a/hudson-core/src/main/java/hudson/model/Job.java b/hudson-core/src/main/java/hudson/model/Job.java
index 62573829..a5e76b86 100644
--- a/hudson-core/src/main/java/hudson/model/Job.java
+++ b/hudson-core/src/main/java/hudson/model/Job.java
@@ -192,12 +192,13 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
* Selected cascadingProject for this job.
*/
protected transient JobT cascadingProject;
- private final static transient ThreadLocal<Boolean> allowSave = new ThreadLocal<Boolean>() {
+ private static class BooleanThreadLocal extends ThreadLocal<Boolean> {
@Override
protected Boolean initialValue() {
return true;
}
- };
+ }
+ private final static transient ThreadLocal<Boolean> allowSave = new BooleanThreadLocal();
protected Job(ItemGroup parent, String name) {
super(parent, name);
diff --git a/hudson-core/src/main/java/hudson/model/Run.java b/hudson-core/src/main/java/hudson/model/Run.java
index 16030ef9..5e8f1701 100644
--- a/hudson-core/src/main/java/hudson/model/Run.java
+++ b/hudson-core/src/main/java/hudson/model/Run.java
@@ -397,8 +397,10 @@ public abstract class Run<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
this.previousBuild = (RunT)key.referenced.getPreviousBuild();
}
finally {
- IS_LOADING_CACHED_VALUES.set(false);
+ IS_LOADING_CACHED_VALUES.remove();
}
+ } else {
+ IS_LOADING_CACHED_VALUES.remove();
}
}
diff --git a/hudson-core/src/main/java/hudson/security/HudsonFilter.java b/hudson-core/src/main/java/hudson/security/HudsonFilter.java
index 6c950755..d9dd6b0b 100644
--- a/hudson-core/src/main/java/hudson/security/HudsonFilter.java
+++ b/hudson-core/src/main/java/hudson/security/HudsonFilter.java
@@ -16,6 +16,7 @@
*/
package hudson.security;
+import hudson.util.ThreadLocalUtils;
import java.io.IOException;
import javax.servlet.Filter;
@@ -155,6 +156,7 @@ public class HudsonFilter implements Filter {
} else {
f.doFilter(request, response, chain);
}
+ ThreadLocalUtils.removeThreadLocals();
}
public void destroy() {
diff --git a/hudson-core/src/main/java/hudson/util/ThreadLocalUtils.java b/hudson-core/src/main/java/hudson/util/ThreadLocalUtils.java
new file mode 100644
index 00000000..a960d280
--- /dev/null
+++ b/hudson-core/src/main/java/hudson/util/ThreadLocalUtils.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2015 Hudson.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Hudson - initial API and implementation and/or initial documentation
+ */
+
+package hudson.util;
+
+import java.lang.ref.Reference;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.util.HashSet;
+import java.util.Set;
+import org.eclipse.hudson.init.InitialSetup;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.context.SecurityContextImpl;
+
+/**
+ *
+ * @author Bob Foster
+ */
+public class ThreadLocalUtils {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ThreadLocalUtils.class);
+
+ private static final boolean VERBOSE = true;
+
+ /**
+ * Remove thread locals created by instance class loaders.
+ *
+ * @param verbose true if log each time threadlocals found or removed; otherwise silent
+ */
+ public static void removeThreadLocals() {
+ Set<ThreadLocal> threadLocals = getThreadLocals(getLoaders());
+ for (ThreadLocal threadLocal : threadLocals) {
+ if (VERBOSE) {
+ Object value = threadLocal.get();
+ LOGGER.info("Removing thread local with key ["+threadLocal+"] and value ["+value+"]");
+ }
+ threadLocal.remove();
+ }
+ }
+
+ private static ClassLoader[] getLoaders() {
+ ClassLoader[] loaders = new ClassLoader[2];
+ loaders[0] = ThreadLocalUtils.class.getClassLoader();
+ loaders[1] = InitialSetup.getHudsonContextClassLoader();
+ return loaders;
+ }
+
+ private static Set<ThreadLocal> getThreadLocals(ClassLoader[] loaders) {
+ Set<ThreadLocal> threadLocals = new HashSet<ThreadLocal>();
+ try {
+ Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
+ threadLocalsField.setAccessible(true);
+ Object threadLocalTable = threadLocalsField.get(Thread.currentThread());
+
+ Class threadLocalMapClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
+ Field tableField = threadLocalMapClass.getDeclaredField("table");
+ tableField.setAccessible(true);
+ Object table = tableField.get(threadLocalTable);
+
+ if (table == null) {
+ return threadLocals;
+ }
+
+ Field referentField = Reference.class.getDeclaredField("referent");
+ referentField.setAccessible(true);
+
+ for (int i=0; i < Array.getLength(table); i++) {
+ Object entry = Array.get(table, i);
+ if (entry != null) {
+ ThreadLocal threadLocal = (ThreadLocal)referentField.get(entry);
+ if (threadLocal != null) {
+ Object value = threadLocal.get();
+ if (value instanceof SecurityContextImpl) {
+ // This may risk removing threadlocals added by other apps,
+ // but perhaps we are doing them a favor.
+ threadLocals.add(threadLocal);
+ } else {
+ ClassLoader tlClassLoader = threadLocal.getClass().getClassLoader();
+ for (ClassLoader cl : loaders) {
+ if (tlClassLoader == cl) {
+ threadLocals.add(threadLocal);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ } catch(Exception e) {
+ LOGGER.warn("Exception getting ThreadLocals in thread "+Thread.currentThread().getName(), e);
+ }
+ return threadLocals;
+ }
+}
diff --git a/hudson-core/src/main/java/hudson/util/TrackedDaemonExecutor.java b/hudson-core/src/main/java/hudson/util/TrackedDaemonExecutor.java
new file mode 100644
index 00000000..82910756
--- /dev/null
+++ b/hudson-core/src/main/java/hudson/util/TrackedDaemonExecutor.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015 Hudson.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Hudson - initial API and implementation and/or initial documentation
+ */
+
+package hudson.util;
+
+/**
+ *
+ * @author Bob Foster
+ */
+public class TrackedDaemonExecutor extends TrackedExecutor {
+
+ public TrackedDaemonExecutor() {
+ factory = new DaemonThreadFactory();
+ }
+
+ /**
+ * Subclass for threads that are not interruptable.
+ */
+ public static class NonInterruptableExecutor extends TrackedDaemonExecutor {
+ @Override
+ public void stopThread(Thread thread) {
+ thread.stop();
+ }
+ }
+}
diff --git a/hudson-core/src/main/java/hudson/util/TrackedExecutor.java b/hudson-core/src/main/java/hudson/util/TrackedExecutor.java
new file mode 100644
index 00000000..67915008
--- /dev/null
+++ b/hudson-core/src/main/java/hudson/util/TrackedExecutor.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2015 Hudson.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Hudson - initial API and implementation and/or initial documentation
+ */
+
+package hudson.util;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.logging.Logger;
+
+/**
+ * TrackedExecutor is an executor that can be created my clients knowing only
+ * <code>TrackedExecutor.class.getName()</code>, e.g., by:
+ * <pre>
+ * Class.forName(executorClassName).newInstance()
+ * </pre>
+ * But all instances of TrackedExecutor and for each instance, all threads
+ * created by TrackedExecutor can be shut down when, e.g., Hudson is
+ * undeployed by its container.
+ * <p>Shutdown is aggressive. If a thread does not respond to <code>interrupt</code>
+ * within 1 ms., the deprecated <code>thread.stop()</code> is called. This
+ * can mean that sometimes thread will be stopped before it has a chance to
+ * respond to interrupt.
+ * @author Bob Foster
+ */
+public class TrackedExecutor implements Executor {
+
+ private static final Logger LOGGER = Logger.getLogger(TrackedExecutor.class.getName());
+
+ /** Default implementation - subclasses may override */
+ protected ThreadFactory factory = Executors.defaultThreadFactory();
+
+ /** Executor list. Must not be weak as executors may be used and forgotten. */
+ private static final List<TrackedExecutor> executors = new ArrayList<TrackedExecutor>();
+ /** Thread list. Must be weak or could hold threads in memory. */
+ private final List<WeakReference<Thread>> threads = new ArrayList<WeakReference<Thread>>();
+ /** True if executor has been shut down */
+ private boolean shutdown;
+
+ /**
+ * Must have a null constructor if it is to be created by
+ * <code>hudson.util.TrackedExecutor.class.newInstance()</code>,
+ * as, e.g., guice does.
+ */
+ public TrackedExecutor() {
+ synchronized (executors) {
+ executors.add(this);
+ }
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ String runnableClass = command.getClass().getName();
+ Thread thread = null;
+ synchronized (threads) {
+ if (shutdown) {
+ LOGGER.severe("Attempt to run "+runnableClass+" after shutdown ignored");
+ throw new IllegalStateException();
+ }
+ thread = factory.newThread(command);
+ thread.setName(runnableClass);
+ threads.add(new WeakReference<Thread>(thread));
+ }
+ thread.start();
+ }
+
+ /**
+ * Shutdown all threads started by any TrackedExecutor whose contextClassLoader
+ * is the same as (==) the argument. TODO not certain
+ * this precaution is necessary, as a TrackedExecutor should
+ * always be created by a different classloader in each instance of
+ * Hudson in a container; but it costs very little to test.
+ *
+ * @param contextClassLoader if not null, only threads with a matching
+ * contextClassLoader are stopped; otherwise, all threads are stopped
+ */
+ public static void shutdownAll(ClassLoader contextClassLoader) {
+ synchronized (executors) {
+ for (Iterator<TrackedExecutor> it = executors.iterator(); it.hasNext();) {
+ TrackedExecutor executor = it.next();
+ executor.shutdown(contextClassLoader);
+ if (executor.threads.isEmpty()) {
+ it.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * Shutdown all threads started by this executor whose contextClassLoader
+ * is the same as (==) the argument.
+ * @param contextClassLoader only threads whose contextClassLoader is ==
+ * are stopped
+ */
+ public void shutdown(ClassLoader contextClassLoader) {
+ synchronized (threads) {
+ shutdown = true;
+ for (Iterator<WeakReference<Thread>> it = threads.iterator(); it.hasNext(); ) {
+ Thread thread = it.next().get();
+ if (thread != null
+ && (contextClassLoader == null
+ || thread.getContextClassLoader() == contextClassLoader)) {
+ stopThread(thread);
+ it.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * Subclass may override if there is a faster or better way to stop threads
+ * for this executor.
+ * @param thread to stop
+ */
+ protected void stopThread(Thread thread) {
+ thread.interrupt();
+ try { thread.join(1); } catch (InterruptedException ignore) { }
+ if (thread.isAlive()) {
+ thread.stop();
+ }
+ }
+
+ /**
+ * Subclass for threads that are not interruptable.
+ */
+ public static class NonInterruptableExecutor extends TrackedExecutor {
+ @Override
+ public void stopThread(Thread thread) {
+ thread.stop();
+ }
+ }
+}
diff --git a/hudson-core/src/main/java/org/eclipse/hudson/HudsonServletContextListener.java b/hudson-core/src/main/java/org/eclipse/hudson/HudsonServletContextListener.java
index 90a16d29..80db47d2 100644
--- a/hudson-core/src/main/java/org/eclipse/hudson/HudsonServletContextListener.java
+++ b/hudson-core/src/main/java/org/eclipse/hudson/HudsonServletContextListener.java
@@ -15,20 +15,21 @@
package org.eclipse.hudson;
+import com.google.common.base.internal.Finalizer;
import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;
import com.thoughtworks.xstream.core.JVM;
+
import hudson.EnvVars;
import hudson.model.Hudson;
import hudson.util.*;
+
import java.io.File;
import java.io.IOException;
-import java.lang.ref.Reference;
-import java.lang.reflect.Array;
-import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.Security;
import java.util.Locale;
+
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
@@ -38,6 +39,7 @@ import javax.servlet.ServletContextListener;
import javax.servlet.ServletResponse;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
+
import org.apache.tools.ant.types.FileSet;
import org.eclipse.hudson.WebAppController.DefaultInstallStrategy;
import org.eclipse.hudson.graph.ChartUtil;
@@ -197,6 +199,8 @@ public final class HudsonServletContextListener implements ServletContextListene
}
}
+ com.google.inject.util.GuiceRuntime.setExecutorClassName(TrackedDaemonExecutor.NonInterruptableExecutor.class.getName());
+
installExpressionFactory(event);
// Do the initial setup (if needed) before actually starting Hudson
@@ -223,6 +227,8 @@ public final class HudsonServletContextListener implements ServletContextListene
}
} else {
initSetup.invokeHudson();
+
+ ThreadLocalUtils.removeThreadLocals();
}
} catch (Exception exc) {
@@ -303,59 +309,24 @@ public final class HudsonServletContextListener implements ServletContextListene
instance.cleanUp();
}
- cleanThreadLocals();
+ ClassLoader loader = InitialSetup.getHudsonContextClassLoader();
- // Logger is in the system classloader, so if we don't do this
- // the whole web app will never be undepoyed.
- java.util.logging.Logger.getLogger("hudson").removeHandler(handler);
- }
-
- private void cleanThreadLocals() {
- String threadName = Thread.currentThread().getName();
try {
- logger.info("Cleaning ThreadLocals in thread "+threadName);
- // Get a reference to the thread locals table of the current thread
- Thread thread = Thread.currentThread();
- Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
- threadLocalsField.setAccessible(true);
- Object threadLocalTable = threadLocalsField.get(thread);
-
- // Get a reference to the array holding the thread local variables inside the
- // ThreadLocalMap of the current thread
- Class threadLocalMapClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
- Field tableField = threadLocalMapClass.getDeclaredField("table");
- tableField.setAccessible(true);
- Object table = tableField.get(threadLocalTable);
-
- if (table == null) {
- logger.info("No ThreadLocalMap in thread "+threadName);
- return;
- }
+ Finalizer.cleanupThreads(loader);
+ } catch (Throwable t) {
+ logger.error("Finalizer cleanup unsuccessful", t);
+ }
- // The key to the ThreadLocalMap is a WeakReference object. The referent field of this object
- // is a reference to the actual ThreadLocal variable
- Field referentField = Reference.class.getDeclaredField("referent");
- referentField.setAccessible(true);
-
- int numRemoved = 0;
- for (int i=0; i < Array.getLength(table); i++) {
- // Each entry in the table array of ThreadLocalMap is an Entry object
- // representing the thread local reference and its value
- Object entry = Array.get(table, i);
- if (entry != null) {
- // Get a reference to the thread local object and remove it from the table
- ThreadLocal threadLocal = (ThreadLocal)referentField.get(entry);
- if (threadLocal != null) {
- threadLocal.remove();
- numRemoved++;
- }
- }
- }
- logger.info("Removed "+numRemoved+" ThreadLocals from thread "+threadName);
- } catch(Exception e) {
- // We will tolerate an exception here and just log it
- logger.warn("Exception cleaning ThreadLocals in thread "+threadName);
- throw new IllegalStateException(e);
+ try {
+ TrackedDaemonExecutor.shutdownAll(loader);
+ } catch (Throwable t) {
+ logger.error("Executor shutdown unsuccessful", t);
}
+
+ ThreadLocalUtils.removeThreadLocals();
+
+ // Logger is in the system classloader, so if we don't do this
+ // the whole web app will never be undepoyed.
+ java.util.logging.Logger.getLogger("hudson").removeHandler(handler);
}
}
diff --git a/hudson-core/src/main/java/org/eclipse/hudson/init/InitialSetup.java b/hudson-core/src/main/java/org/eclipse/hudson/init/InitialSetup.java
index 47ff434c..c5e43c0e 100644
--- a/hudson-core/src/main/java/org/eclipse/hudson/init/InitialSetup.java
+++ b/hudson-core/src/main/java/org/eclipse/hudson/init/InitialSetup.java
@@ -24,6 +24,7 @@ import hudson.security.Permission;
import hudson.util.DaemonThreadFactory;
import hudson.util.HudsonIsLoading;
import hudson.util.VersionNumber;
+
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -41,9 +42,11 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
+
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
+
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.eclipse.hudson.WebAppController;
@@ -98,12 +101,13 @@ final public class InitialSetup {
}
}));
private final HudsonSecurityManager hudsonSecurityManager;
- private final XmlFile initSetupFile;
+ private static XmlFile initSetupFile;
private final File hudsonHomeDir;
private boolean proxyNeeded = false;
private final List<PluginInstallationJob> installationsJobs = new CopyOnWriteArrayList<PluginInstallationJob>();
private static ClassLoader outerClassLoader;
private static ClassLoader initialClassLoader;
+ private static Thread initThread;
private static int highInitThreadNumber = 0;
private static InitialSetup INSTANCE;
@@ -315,7 +319,7 @@ final public class InitialSetup {
return HttpResponses.ok();
}
- private class OuterClassLoader extends ClassLoader {
+ private static class OuterClassLoader extends ClassLoader {
OuterClassLoader(ClassLoader parent) {
super(parent);
@@ -345,7 +349,7 @@ final public class InitialSetup {
InitialRunnable initialRunnable = (InitialRunnable) ctor.newInstance(controller, logger, hudsonHomeDir, servletContext, restart);
controller.install(hudsonIsLoading);
- Thread initThread = new Thread(initialRunnable, "hudson initialization thread " + (++highInitThreadNumber));
+ initThread = new Thread(initialRunnable, "hudson initialization thread " + (++highInitThreadNumber));
initThread.setContextClassLoader(outerClassLoader);
initThread.start();
@@ -360,6 +364,10 @@ final public class InitialSetup {
*/
}
+ public static ClassLoader getHudsonContextClassLoader() {
+ return outerClassLoader;
+ }
+
private static class ErrorHttpResponse implements HttpResponse {
private String message;
diff --git a/pom.xml b/pom.xml
index 04399111..cc0dcccb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -115,7 +115,7 @@
<jetty.javax.serverlet.version>3.1.0</jetty.javax.serverlet.version>
<maven-hpi-plugin.version>3.0.3</maven-hpi-plugin.version>
- <stapler.version>3.0.3</stapler.version>
+ <stapler.version>3.0.4</stapler.version>
<maven-stapler-plugin.version>3.0.2</maven-stapler-plugin.version>
<!--GWT properties-->
@@ -135,9 +135,8 @@
<servlet-api.version>2.4</servlet-api.version>
<slf4j.version>1.6.1</slf4j.version>
<logback-classic.version>0.9.28</logback-classic.version>
- <guava.version>14.0.1</guava.version>
- <guava.groupid>com.google.guava</guava.groupid>
- <!--guava.groupid>org.hudsonci.lib</guava.groupid-->
+ <guava.version>14.0.1-h-1</guava.version>
+ <guava.groupid>org.hudsonci.lib.guava</guava.groupid>
<ant.version>1.8.2</ant.version>
<xstream.version>1.4.8</xstream.version>
<jsr305.version>1.3.9</jsr305.version>

Back to the top