Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBob Foster2015-04-26 00:31:40 -0400
committerBob Foster2015-04-26 00:31:40 -0400
commit51bb533a0a9321d7794498ca6f6360dbe862a022 (patch)
tree88ba874edd9795336899ce7635eeddad6651c276
parent0574c8a5adf8f0aff59c68d0b52b4b01d4cfbb25 (diff)
downloadorg.eclipse.hudson.core-51bb533a0a9321d7794498ca6f6360dbe862a022.tar.gz
org.eclipse.hudson.core-51bb533a0a9321d7794498ca6f6360dbe862a022.tar.xz
org.eclipse.hudson.core-51bb533a0a9321d7794498ca6f6360dbe862a022.zip
Remove threadlocals for SimpleDateFormat
-rw-r--r--hudson-core/pom.xml11
-rw-r--r--hudson-core/src/main/java/hudson/model/Hudson.java14
-rw-r--r--hudson-core/src/main/java/hudson/model/Run.java20
-rw-r--r--hudson-core/src/main/java/hudson/model/RunMap.java2
-rw-r--r--hudson-core/src/main/java/org/eclipse/hudson/HudsonServletContextListener.java79
-rw-r--r--hudson-inject/pom.xml2
-rw-r--r--hudson-test-utils/pom.xml2
-rw-r--r--hudson-utils/pom.xml2
-rw-r--r--pom.xml12
9 files changed, 108 insertions, 36 deletions
diff --git a/hudson-core/pom.xml b/hudson-core/pom.xml
index 1bb2ab21..b3a26b38 100644
--- a/hudson-core/pom.xml
+++ b/hudson-core/pom.xml
@@ -422,15 +422,18 @@
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
- <!-- hudson doesn't use this directly, but some plugins wanted to use the latest -->
+ <!-- hudson now uses this directly -->
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
+ <version>${commons-lang.version}</version>
</dependency>
- <dependency><!-- Left for backward compatibility-->
+ <!-- Plugins that depend on old versions of commons-lang will need to
+ update or add the older dependency -->
+ <!--dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.4</version>
- </dependency>
+ </dependency-->
<dependency><!-- Left for backward compatibility-->
<groupId>commons-digester</groupId>
@@ -644,7 +647,7 @@
</dependency>
<dependency>
- <groupId>com.google.guava</groupId>
+ <groupId>${guava.groupid}</groupId>
<artifactId>guava</artifactId>
</dependency>
diff --git a/hudson-core/src/main/java/hudson/model/Hudson.java b/hudson-core/src/main/java/hudson/model/Hudson.java
index 73b2b6e4..f894da16 100644
--- a/hudson-core/src/main/java/hudson/model/Hudson.java
+++ b/hudson-core/src/main/java/hudson/model/Hudson.java
@@ -19,6 +19,7 @@
package hudson.model;
import com.thoughtworks.xstream.XStream;
+
import hudson.BulkChange;
import hudson.DNSMultiCast;
import hudson.DescriptorExtensionList;
@@ -50,7 +51,6 @@ import hudson.cli.CliManagerImpl;
import hudson.cli.declarative.CLIMethod;
import hudson.cli.declarative.CLIResolver;
import hudson.init.InitMilestone;
-
import static hudson.init.InitMilestone.*;
import hudson.init.InitReactorListener;
import hudson.init.InitStrategy;
@@ -113,6 +113,7 @@ import hudson.views.DefaultViewsTabBar;
import hudson.views.MyViewsTabBar;
import hudson.views.ViewsTabBar;
import hudson.widgets.Widget;
+
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -136,6 +137,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.TimeZone;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
@@ -159,18 +161,22 @@ import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+
import javax.crypto.SecretKey;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
+
import static javax.servlet.http.HttpServletResponse.*;
import net.sf.json.JSONObject;
+
import org.antlr.runtime.RecognitionException;
import org.apache.commons.jelly.JellyException;
import org.apache.commons.jelly.Script;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.FastDateFormat;
import org.apache.commons.logging.LogFactory;
import org.eclipse.hudson.WebAppController;
import org.eclipse.hudson.plugins.PluginCenter;
@@ -523,6 +529,12 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
return redirect;
}
};
+
+ public static class HudsonDateFormat extends FastDateFormat {
+ public HudsonDateFormat(String format) {
+ super(format, TimeZone.getDefault(), Locale.getDefault());
+ }
+ }
@CLIResolver
public static Hudson getInstance() {
diff --git a/hudson-core/src/main/java/hudson/model/Run.java b/hudson-core/src/main/java/hudson/model/Run.java
index 329a0943..16030ef9 100644
--- a/hudson-core/src/main/java/hudson/model/Run.java
+++ b/hudson-core/src/main/java/hudson/model/Run.java
@@ -79,6 +79,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.HashSet;
+import java.util.TimeZone;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;
@@ -87,9 +88,11 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONObject;
+
import org.apache.commons.io.input.NullInputStream;
import org.apache.commons.io.IOUtils;
import org.apache.commons.jelly.XMLOutput;
+import org.apache.commons.lang3.time.FastDateFormat;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses;
import org.kohsuke.stapler.QueryParameter;
@@ -99,6 +102,7 @@ import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import com.thoughtworks.xstream.XStream;
+
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
@@ -218,13 +222,8 @@ public abstract class Run<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
* This field is not persisted.
*/
private volatile transient Runner runner;
- protected static final ThreadLocal<SimpleDateFormat> ID_FORMATTER =
- new ThreadLocal<SimpleDateFormat>() {
- @Override
- protected SimpleDateFormat initialValue() {
- return new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
- }
- };
+
+ protected static final Hudson.HudsonDateFormat ID_FORMATTER = new Hudson.HudsonDateFormat("yyyy-MM-dd_HH-mm-ss");
/**
* State when a Run is being created from cached values.
@@ -316,7 +315,7 @@ public abstract class Run<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
/*package*/ static long parseTimestampFromBuildDir(File buildDir) throws IOException {
try {
- return ID_FORMATTER.get().parse(buildDir.getName()).getTime();
+ return ID_FORMATTER.parse(buildDir.getName()).getTime();
} catch (ParseException e) {
throw new IOException2("Invalid directory name " + buildDir, e);
} catch (NumberFormatException e) {
@@ -908,16 +907,17 @@ public abstract class Run<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
*/
@Exported
public String getId() {
- return ID_FORMATTER.get().format(new Date(timestamp));
+ return ID_FORMATTER.format(new Date(timestamp));
}
/**
* Get the date formatter used to convert the directory name in to a
* timestamp This is nasty exposure of private data, but needed all the time
* the directory containing the build is used as it's timestamp.
+ * @since 3.3.0 creates new DateFormat on each call to eliminate ThreadLocal
*/
public static DateFormat getIDFormatter() {
- return ID_FORMATTER.get();
+ return new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
}
public Descriptor getDescriptorByName(String className) {
diff --git a/hudson-core/src/main/java/hudson/model/RunMap.java b/hudson-core/src/main/java/hudson/model/RunMap.java
index 52c17b38..d9bf35a3 100644
--- a/hudson-core/src/main/java/hudson/model/RunMap.java
+++ b/hudson-core/src/main/java/hudson/model/RunMap.java
@@ -590,7 +590,7 @@ public final class RunMap<J extends Job<J, R>, R extends Run<J, R>>
if ( !loadFromRunMapXml(job, cons)) {
- final SimpleDateFormat formatter = Run.ID_FORMATTER.get();
+ final Hudson.HudsonDateFormat formatter = Run.ID_FORMATTER;
TreeMap<Integer, RunValue<J,R>> m = new TreeMap<Integer, RunValue<J,R>>(BUILD_TIME_COMPARATOR);
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 38e9271f..90a16d29 100644
--- a/hudson-core/src/main/java/org/eclipse/hudson/HudsonServletContextListener.java
+++ b/hudson-core/src/main/java/org/eclipse/hudson/HudsonServletContextListener.java
@@ -20,13 +20,15 @@ import com.thoughtworks.xstream.core.JVM;
import hudson.EnvVars;
import hudson.model.Hudson;
import hudson.util.*;
-import org.eclipse.hudson.WebAppController.DefaultInstallStrategy;
-import org.jvnet.localizer.LocaleProvider;
-import org.kohsuke.stapler.Stapler;
-import org.kohsuke.stapler.StaplerRequest;
-import org.kohsuke.stapler.jelly.JellyFacet;
-import org.apache.tools.ant.types.FileSet;
-
+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;
@@ -36,17 +38,17 @@ import javax.servlet.ServletContextListener;
import javax.servlet.ServletResponse;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
-import java.io.File;
-import java.io.IOException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.Locale;
-import java.security.Security;
+import org.apache.tools.ant.types.FileSet;
+import org.eclipse.hudson.WebAppController.DefaultInstallStrategy;
import org.eclipse.hudson.graph.ChartUtil;
import org.eclipse.hudson.init.InitialSetup;
import org.eclipse.hudson.init.InitialSetupLogin;
import org.eclipse.hudson.security.HudsonSecurityEntitiesHolder;
import org.eclipse.hudson.security.HudsonSecurityManager;
+import org.jvnet.localizer.LocaleProvider;
+import org.kohsuke.stapler.Stapler;
+import org.kohsuke.stapler.StaplerRequest;
+import org.kohsuke.stapler.jelly.JellyFacet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -301,8 +303,59 @@ public final class HudsonServletContextListener implements ServletContextListene
instance.cleanUp();
}
+ cleanThreadLocals();
+
// 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;
+ }
+
+ // 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);
+ }
+ }
}
diff --git a/hudson-inject/pom.xml b/hudson-inject/pom.xml
index f992a67f..697e4e8b 100644
--- a/hudson-inject/pom.xml
+++ b/hudson-inject/pom.xml
@@ -48,7 +48,7 @@
</dependency>
<dependency>
- <groupId>com.google.guava</groupId>
+ <groupId>${guava.groupid}</groupId>
<artifactId>guava</artifactId>
<exclusions>
<exclusion>
diff --git a/hudson-test-utils/pom.xml b/hudson-test-utils/pom.xml
index 00ec9076..8215b9ae 100644
--- a/hudson-test-utils/pom.xml
+++ b/hudson-test-utils/pom.xml
@@ -43,7 +43,7 @@
</dependency>
<dependency>
- <groupId>com.google.guava</groupId>
+ <groupId>${guava.groupid}</groupId>
<artifactId>guava</artifactId>
</dependency>
diff --git a/hudson-utils/pom.xml b/hudson-utils/pom.xml
index 9471c085..df7790f5 100644
--- a/hudson-utils/pom.xml
+++ b/hudson-utils/pom.xml
@@ -48,7 +48,7 @@
</dependency>
<dependency>
- <groupId>com.google.guava</groupId>
+ <groupId>${guava.groupid}</groupId>
<artifactId>guava</artifactId>
</dependency>
diff --git a/pom.xml b/pom.xml
index 64f75e53..63df9a1d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -115,8 +115,10 @@
<jetty.javax.serverlet.version>3.1.0</jetty.javax.serverlet.version>
<maven-hpi-plugin.version>3.0.2</maven-hpi-plugin.version>
- <stapler.version>3.0.2</stapler.version>
- <maven-stapler-plugin.version>3.0.1</maven-stapler-plugin.version>
+ <!--stapler.version>3.0.2</stapler.version>
+ <maven-stapler-plugin.version>3.0.1</maven-stapler-plugin.version-->
+ <stapler.version>3.0.3-SNAPSHOT</stapler.version>
+ <maven-stapler-plugin.version>3.0.2-SNAPSHOT</maven-stapler-plugin.version>
<!--GWT properties-->
<gwt.draftCompile>false</gwt.draftCompile>
@@ -136,10 +138,12 @@
<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>
<ant.version>1.8.2</ant.version>
<xstream.version>1.4.8</xstream.version>
<jsr305.version>1.3.9</jsr305.version>
- <commons-lang.version>3.0.1</commons-lang.version>
+ <commons-lang.version>3.4</commons-lang.version>
<commons-io.version>2.0.1</commons-io.version>
<commons-codec.version>1.4</commons-codec.version>
<aether.version>1.11</aether.version>
@@ -708,7 +712,7 @@
</dependency>
<dependency>
- <groupId>com.google.guava</groupId>
+ <groupId>${guava.groupid}</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>

Back to the top