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-ffa450edef68tags/REL_5_2_4
import org.apache.poi.util.GenericRecordUtil; | import org.apache.poi.util.GenericRecordUtil; | ||||
import org.apache.poi.util.IOUtils; | import org.apache.poi.util.IOUtils; | ||||
import org.apache.poi.util.Internal; | import org.apache.poi.util.Internal; | ||||
import org.apache.poi.util.ThreadLocalUtil; | |||||
import org.apache.poi.util.Units; | import org.apache.poi.util.Units; | ||||
/** | /** | ||||
INIT, LOADED | INIT, LOADED | ||||
} | } | ||||
private static final ThreadLocal<LoadSavePhase> loadSavePhase = new ThreadLocal<>(); | 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 | // What we're based on | ||||
private final HSLFSlideShowImpl _hslfSlideShow; | private final HSLFSlideShowImpl _hslfSlideShow; |
import org.apache.poi.poifs.filesystem.FileMagic; | import org.apache.poi.poifs.filesystem.FileMagic; | ||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | import org.apache.poi.poifs.filesystem.POIFSFileSystem; | ||||
import org.apache.poi.util.IOUtils; | import org.apache.poi.util.IOUtils; | ||||
import org.apache.poi.util.ThreadLocalUtil; | |||||
/** | /** | ||||
* Figures out the correct POIOLE2TextExtractor for your supplied | * Figures out the correct POIOLE2TextExtractor for your supplied | ||||
/** Should this thread prefer event based over usermodel based extractors? */ | /** Should this thread prefer event based over usermodel based extractors? */ | ||||
private static final ThreadLocal<Boolean> threadPreferEventExtractors = ThreadLocal.withInitial(() -> Boolean.FALSE); | 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? */ | /** Should all threads prefer event based over usermodel based extractors? */ | ||||
private static Boolean allPreferEventExtractors; | private static Boolean allPreferEventExtractors; |
package org.apache.poi.hssf.record.crypto; | package org.apache.poi.hssf.record.crypto; | ||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook; | import org.apache.poi.hssf.usermodel.HSSFWorkbook; | ||||
import org.apache.poi.util.ThreadLocalUtil; | |||||
public final class Biff8EncryptionKey { | public final class Biff8EncryptionKey { | ||||
/** | /** | ||||
* (e.g. {@link HSSFWorkbook}) that need this functionality. | * (e.g. {@link HSSFWorkbook}) that need this functionality. | ||||
*/ | */ | ||||
private static final ThreadLocal<String> _userPasswordTLS = new ThreadLocal<>(); | 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. | * Sets the BIFF8 encryption/decryption password for the current thread. |
import org.apache.poi.ss.usermodel.HorizontalAlignment; | import org.apache.poi.ss.usermodel.HorizontalAlignment; | ||||
import org.apache.poi.ss.usermodel.VerticalAlignment; | import org.apache.poi.ss.usermodel.VerticalAlignment; | ||||
import org.apache.poi.util.Removal; | 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. | * High level representation of the style of a cell in a sheet of a workbook. | ||||
private static final ThreadLocal<Short> lastDateFormat = ThreadLocal.withInitial(() -> Short.MIN_VALUE); | private static final ThreadLocal<Short> lastDateFormat = ThreadLocal.withInitial(() -> Short.MIN_VALUE); | ||||
private static final ThreadLocal<List<FormatRecord>> lastFormats = new ThreadLocal<>(); | private static final ThreadLocal<List<FormatRecord>> lastFormats = new ThreadLocal<>(); | ||||
private static final ThreadLocal<String> getDataFormatStringCache = 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 | * Get the contents of the format string, by looking up | ||||
_format.setFillBackground(bg); | _format.setFillBackground(bg); | ||||
checkDefaultBackgroundFills(); | checkDefaultBackgroundFills(); | ||||
} | } | ||||
/** | /** | ||||
* Set the background fill color represented as a {@link org.apache.poi.ss.usermodel.Color} value. | * Set the background fill color represented as a {@link org.apache.poi.ss.usermodel.Color} value. | ||||
* <br> | * <br> | ||||
_format.setFillForeground(bg); | _format.setFillForeground(bg); | ||||
checkDefaultBackgroundFills(); | checkDefaultBackgroundFills(); | ||||
} | } | ||||
/** | /** | ||||
* Set the foreground fill color represented as a {@link org.apache.poi.ss.usermodel.Color} value. | * Set the foreground fill color represented as a {@link org.apache.poi.ss.usermodel.Color} value. | ||||
* <br> | * <br> |
import org.apache.poi.sl.usermodel.TextBox; | import org.apache.poi.sl.usermodel.TextBox; | ||||
import org.apache.poi.sl.usermodel.TextParagraph; | import org.apache.poi.sl.usermodel.TextParagraph; | ||||
import org.apache.poi.sl.usermodel.TextShape; | import org.apache.poi.sl.usermodel.TextShape; | ||||
import org.apache.poi.util.ThreadLocalUtil; | |||||
public class DrawFactory { | public class DrawFactory { | ||||
private static final ThreadLocal<DrawFactory> defaultFactory = new ThreadLocal<>(); | 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. | * Set a custom draw factory for the current thread. |
import org.apache.poi.ss.formula.ConditionalFormattingEvaluator; | import org.apache.poi.ss.formula.ConditionalFormattingEvaluator; | ||||
import org.apache.poi.util.LocaleUtil; | import org.apache.poi.util.LocaleUtil; | ||||
import org.apache.poi.util.ThreadLocalUtil; | |||||
/** | /** | ||||
* Contains methods for dealing with Excel dates. | * Contains methods for dealing with Excel dates. | ||||
private static final ThreadLocal<Integer> lastFormatIndex = ThreadLocal.withInitial(() -> -1); | private static final ThreadLocal<Integer> lastFormatIndex = ThreadLocal.withInitial(() -> -1); | ||||
private static final ThreadLocal<String> lastFormatString = new ThreadLocal<>(); | private static final ThreadLocal<String> lastFormatString = new ThreadLocal<>(); | ||||
private static final ThreadLocal<Boolean> lastCachedResult = 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) { | private static boolean isCached(String formatString, int formatIndex) { | ||||
return formatIndex == lastFormatIndex.get() | return formatIndex == lastFormatIndex.get() |
private static final ThreadLocal<TimeZone> userTimeZone = new ThreadLocal<>(); | private static final ThreadLocal<TimeZone> userTimeZone = new ThreadLocal<>(); | ||||
private static final ThreadLocal<Locale> userLocale = 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 | * As time zone information is not stored in any format, it can be |
/* ==================================================================== | |||||
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); | |||||
} | |||||
} |
/* ==================================================================== | |||||
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(); | |||||
} | |||||
} |