Bug 66521: Add a utility to clear all thread locals

Otherwise some applications may complain about left-over things,
e.g. Tomcat sometimes reports warning logs if Threads are not
cleaned up before being passed back into the global thread-pool.

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1908263 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Dominik Stadler 2023-03-11 06:49:12 +00:00
parent f192c95e0f
commit b8cc997cd0
9 changed files with 167 additions and 2 deletions

View File

@ -66,6 +66,7 @@ import org.apache.poi.sl.usermodel.SlideShow;
import org.apache.poi.util.GenericRecordUtil;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Internal;
import org.apache.poi.util.ThreadLocalUtil;
import org.apache.poi.util.Units;
/**
@ -92,6 +93,10 @@ public final class HSLFSlideShow extends POIDocument implements SlideShow<HSLFSh
INIT, LOADED
}
private static final ThreadLocal<LoadSavePhase> loadSavePhase = new ThreadLocal<>();
static {
// allow to clear all thread-locals via ThreadLocalUtil
ThreadLocalUtil.registerCleaner(loadSavePhase::remove);
}
// What we're based on
private final HSLFSlideShowImpl _hslfSlideShow;

View File

@ -38,6 +38,7 @@ import org.apache.poi.poifs.filesystem.Entry;
import org.apache.poi.poifs.filesystem.FileMagic;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.ThreadLocalUtil;
/**
* Figures out the correct POIOLE2TextExtractor for your supplied
@ -64,6 +65,10 @@ public final class ExtractorFactory {
/** Should this thread prefer event based over usermodel based extractors? */
private static final ThreadLocal<Boolean> threadPreferEventExtractors = ThreadLocal.withInitial(() -> Boolean.FALSE);
static {
// allow to clear all thread-locals via ThreadLocalUtil
ThreadLocalUtil.registerCleaner(threadPreferEventExtractors::remove);
}
/** Should all threads prefer event based over usermodel based extractors? */
private static Boolean allPreferEventExtractors;

View File

@ -17,6 +17,7 @@
package org.apache.poi.hssf.record.crypto;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.util.ThreadLocalUtil;
public final class Biff8EncryptionKey {
/**
@ -25,6 +26,10 @@ public final class Biff8EncryptionKey {
* (e.g. {@link HSSFWorkbook}) that need this functionality.
*/
private static final ThreadLocal<String> _userPasswordTLS = new ThreadLocal<>();
static {
// allow to clear all thread-locals via ThreadLocalUtil
ThreadLocalUtil.registerCleaner(_userPasswordTLS::remove);
}
/**
* Sets the BIFF8 encryption/decryption password for the current thread.

View File

@ -36,6 +36,7 @@ import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.util.Removal;
import org.apache.poi.util.ThreadLocalUtil;
/**
* High level representation of the style of a cell in a sheet of a workbook.
@ -123,6 +124,14 @@ public final class HSSFCellStyle implements CellStyle, Duplicatable {
private static final ThreadLocal<Short> lastDateFormat = ThreadLocal.withInitial(() -> Short.MIN_VALUE);
private static final ThreadLocal<List<FormatRecord>> lastFormats = new ThreadLocal<>();
private static final ThreadLocal<String> getDataFormatStringCache = new ThreadLocal<>();
static {
// allow to clear all thread-locals via ThreadLocalUtil
ThreadLocalUtil.registerCleaner(() -> {
lastDateFormat.remove();
lastFormats.remove();
getDataFormatStringCache.remove();
});
}
/**
* Get the contents of the format string, by looking up
@ -637,7 +646,7 @@ public final class HSSFCellStyle implements CellStyle, Duplicatable {
_format.setFillBackground(bg);
checkDefaultBackgroundFills();
}
/**
* Set the background fill color represented as a {@link org.apache.poi.ss.usermodel.Color} value.
* <br>
@ -696,7 +705,7 @@ public final class HSSFCellStyle implements CellStyle, Duplicatable {
_format.setFillForeground(bg);
checkDefaultBackgroundFills();
}
/**
* Set the foreground fill color represented as a {@link org.apache.poi.ss.usermodel.Color} value.
* <br>

View File

@ -38,9 +38,14 @@ import org.apache.poi.sl.usermodel.TableShape;
import org.apache.poi.sl.usermodel.TextBox;
import org.apache.poi.sl.usermodel.TextParagraph;
import org.apache.poi.sl.usermodel.TextShape;
import org.apache.poi.util.ThreadLocalUtil;
public class DrawFactory {
private static final ThreadLocal<DrawFactory> defaultFactory = new ThreadLocal<>();
static {
// allow to clear all thread-locals via ThreadLocalUtil
ThreadLocalUtil.registerCleaner(defaultFactory::remove);
}
/**
* Set a custom draw factory for the current thread.

View File

@ -36,6 +36,7 @@ import java.util.regex.Pattern;
import org.apache.poi.ss.formula.ConditionalFormattingEvaluator;
import org.apache.poi.util.LocaleUtil;
import org.apache.poi.util.ThreadLocalUtil;
/**
* Contains methods for dealing with Excel dates.
@ -546,6 +547,14 @@ public class DateUtil {
private static final ThreadLocal<Integer> lastFormatIndex = ThreadLocal.withInitial(() -> -1);
private static final ThreadLocal<String> lastFormatString = new ThreadLocal<>();
private static final ThreadLocal<Boolean> lastCachedResult = new ThreadLocal<>();
static {
// allow to clear all thread-locals via ThreadLocalUtil
ThreadLocalUtil.registerCleaner(() -> {
lastFormatIndex.remove();
lastFormatString.remove();
lastCachedResult.remove();
});
}
private static boolean isCached(String formatString, int formatIndex) {
return formatIndex == lastFormatIndex.get()

View File

@ -57,6 +57,13 @@ public final class LocaleUtil {
private static final ThreadLocal<TimeZone> userTimeZone = new ThreadLocal<>();
private static final ThreadLocal<Locale> userLocale = new ThreadLocal<>();
static {
// allow to clear all thread-locals via ThreadLocalUtil
ThreadLocalUtil.registerCleaner(() -> {
userTimeZone.remove();
userLocale.remove();
});
}
/**
* As time zone information is not stored in any format, it can be

View File

@ -0,0 +1,63 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.util;
import java.util.ArrayList;
import java.util.List;
/**
* Small utility to allow to remove references held in ThreadLocals.
*
* This is sometimes necessary, e.g. when returning threads into a global
* thread pool.
*
* For each usage of ThreadLocal, a cleaner is registered via
* registerCleaner().
*/
public class ThreadLocalUtil {
private final static List<Runnable> registeredCleaners = new ArrayList<>();
private ThreadLocalUtil() {
}
/**
* Clear {@link ThreadLocal}s of the current thread.
*
* This can be used to clean out a thread before "returning"
* it to a thread-pool or a Web-Container like Tomcat.
*
* Usually org.apache.xmlbeans.ThreadLocalUtil#clearAllThreadLocals()
* should be called as well to clear out some more ThreadLocals which
* are created by the XMLBeans library internally.
*/
public static void clearAllThreadLocals() {
// run all registered cleaners
registeredCleaners.forEach(Runnable::run);
}
/**
* Intended for internal use only so other modules of Apache POi
* can add cleaners.
*
* @param cleaner a runnable which clears some thread-local that is
* located outside of the "poi" module.
*/
@Internal
public static void registerCleaner(Runnable cleaner) {
registeredCleaners.add(cleaner);
}
}

View File

@ -0,0 +1,57 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.apache.poi.sl.draw.DrawFactory;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
public class TestThreadLocalUtil {
private final MemoryLeakVerifier verifier = new MemoryLeakVerifier();
@AfterEach
void tearDown() {
verifier.assertGarbageCollected();
}
@Test
public void testClearThreadLocalsNoData() {
// simply calling it without any thread locals should work
ThreadLocalUtil.clearAllThreadLocals();
}
@Test
public void testClearThreadLocalsWithData() {
DrawFactory factory = new DrawFactory();
// use the memory leak verifier to ensure that the thread-local is
// released after the clear-call below
verifier.addObject(factory);
// store the object in a thread-local
DrawFactory.setDefaultFactory(factory);
// retrieving it works now
assertEquals(factory, DrawFactory.getInstance(null));
// then clear them so that the verifier in tearDown() does not
// see the reference any longer
ThreadLocalUtil.clearAllThreadLocals();
}
}