WW-4762 Extracts base abstract class

This commit is contained in:
Lukasz Lenart
2017-04-26 12:24:57 +02:00
parent 8bf77a1d19
commit 2e23d7a07e
2 changed files with 363 additions and 360 deletions
@@ -0,0 +1,355 @@
package com.opensymphony.xwork2.util;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.LocalizedTextProvider;
import com.opensymphony.xwork2.inject.Inject;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
public abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
private static final Logger LOG = LogManager.getLogger(AbstractLocalizedTextProvider.class);
public static final String XWORK_MESSAGES_BUNDLE = "com/opensymphony/xwork2/xwork-messages";
public static final String STRUTS_MESSAGES_BUNDLE = "org/apache/struts2/struts-messages";
private static final String TOMCAT_RESOURCE_ENTRIES_FIELD = "resourceEntries";
private final String RELOADED = "com.opensymphony.xwork2.util.LocalizedTextProvider.reloaded";
protected final ConcurrentMap<String, ResourceBundle> bundlesMap = new ConcurrentHashMap<>();
protected boolean devMode = false;
protected boolean reloadBundles = false;
private final ConcurrentMap<MessageFormatKey, MessageFormat> messageFormats = new ConcurrentHashMap<>();
private final ConcurrentMap<Integer, List<String>> classLoaderMap = new ConcurrentHashMap<>();
private final Set<String> missingBundles = Collections.synchronizedSet(new HashSet<String>());
private final ConcurrentMap<Integer, ClassLoader> delegatedClassLoaderMap = new ConcurrentHashMap<>();
/**
* Add's the bundle to the internal list of default bundles.
* If the bundle already exists in the list it will be re-added.
*
* @param resourceBundleName the name of the bundle to add.
*/
@Override
public void addDefaultResourceBundle(String resourceBundleName) {
//make sure this doesn't get added more than once
final ClassLoader ccl = getCurrentThreadContextClassLoader();
synchronized (XWORK_MESSAGES_BUNDLE) {
List<String> bundles = classLoaderMap.get(ccl.hashCode());
if (bundles == null) {
bundles = new CopyOnWriteArrayList<>();
classLoaderMap.put(ccl.hashCode(), bundles);
}
bundles.remove(resourceBundleName);
bundles.add(0, resourceBundleName);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Added default resource bundle '{}' to default resource bundles for the following classloader '{}'", resourceBundleName, ccl.toString());
}
}
protected List<String> getCurrentBundleNames() {
return classLoaderMap.get(getCurrentThreadContextClassLoader().hashCode());
}
protected ClassLoader getCurrentThreadContextClassLoader() {
return Thread.currentThread().getContextClassLoader();
}
/**
* Returns a localized message for the specified key, aTextName. Neither the key nor the
* message is evaluated.
*
* @param aTextName the message key
* @param locale the locale the message should be for
* @return a localized message based on the specified key, or null if no localized message can be found for it
*/
@Override
public String findDefaultText(String aTextName, Locale locale) {
List<String> localList = getCurrentBundleNames();
for (String bundleName : localList) {
ResourceBundle bundle = findResourceBundle(bundleName, locale);
if (bundle != null) {
reloadBundles();
try {
return bundle.getString(aTextName);
} catch (MissingResourceException e) {
// will be logged when not found in any bundle
}
}
}
if (devMode) {
LOG.warn("Missing key [{}] in bundles [{}]!", aTextName, localList);
} else {
LOG.debug("Missing key [{}] in bundles [{}]!", aTextName, localList);
}
return null;
}
/**
* Returns a localized message for the specified key, aTextName, substituting variables from the
* array of params into the message. Neither the key nor the message is evaluated.
*
* @param aTextName the message key
* @param locale the locale the message should be for
* @param params an array of objects to be substituted into the message text
* @return A formatted message based on the specified key, or null if no localized message can be found for it
*/
@Override
public String findDefaultText(String aTextName, Locale locale, Object[] params) {
String defaultText = findDefaultText(aTextName, locale);
if (defaultText != null) {
MessageFormat mf = buildMessageFormat(defaultText, locale);
return formatWithNullDetection(mf, params);
}
return null;
}
/**
* @param classLoader a {@link ClassLoader} to look up the bundle from if none can be found on the current thread's classloader
*/
public void setDelegatedClassLoader(final ClassLoader classLoader) {
synchronized (bundlesMap) {
delegatedClassLoaderMap.put(getCurrentThreadContextClassLoader().hashCode(), classLoader);
}
}
/**
* @param bundleName Removes the bundle from any cached "misses"
*/
public void clearBundle(final String bundleName) {
bundlesMap.remove(getCurrentThreadContextClassLoader().hashCode() + bundleName);
}
protected void reloadBundles() {
reloadBundles(ActionContext.getContext() != null ? ActionContext.getContext().getContextMap() : null);
}
protected void reloadBundles(Map<String, Object> context) {
if (reloadBundles) {
try {
Boolean reloaded;
if (context != null) {
reloaded = (Boolean) ObjectUtils.defaultIfNull(context.get(RELOADED), Boolean.FALSE);
} else {
reloaded = Boolean.FALSE;
}
if (!reloaded) {
bundlesMap.clear();
try {
clearMap(ResourceBundle.class, null, "cacheList");
} catch (NoSuchFieldException e) {
// happens in IBM JVM, that has a different ResourceBundle impl
// it has a 'cache' member
clearMap(ResourceBundle.class, null, "cache");
}
// now, for the true and utter hack, if we're running in tomcat, clear
// it's class loader resource cache as well.
clearTomcatCache();
if (context != null) {
context.put(RELOADED, true);
}
LOG.debug("Resource bundles reloaded");
}
} catch (Exception e) {
LOG.error("Could not reload resource bundles", e);
}
}
}
private void clearTomcatCache() {
ClassLoader loader = getCurrentThreadContextClassLoader();
// no need for compilation here.
Class cl = loader.getClass();
try {
if ("org.apache.catalina.loader.WebappClassLoader".equals(cl.getName())) {
clearMap(cl, loader, TOMCAT_RESOURCE_ENTRIES_FIELD);
} else {
LOG.debug("Class loader {} is not tomcat loader.", cl.getName());
}
} catch (NoSuchFieldException nsfe) {
if ("org.apache.catalina.loader.WebappClassLoaderBase".equals(cl.getSuperclass().getName())) {
LOG.debug("Base class {} doesn't contain '{}' field, trying with parent!", cl.getName(), TOMCAT_RESOURCE_ENTRIES_FIELD, nsfe);
try {
clearMap(cl.getSuperclass(), loader, TOMCAT_RESOURCE_ENTRIES_FIELD);
} catch (Exception e) {
LOG.warn("Couldn't clear tomcat cache using {}", cl.getSuperclass().getName(), e);
}
}
} catch (Exception e) {
LOG.warn("Couldn't clear tomcat cache", cl.getName(), e);
}
}
private void clearMap(Class cl, Object obj, String name)
throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Field field = cl.getDeclaredField(name);
field.setAccessible(true);
Object cache = field.get(obj);
synchronized (cache) {
Class ccl = cache.getClass();
Method clearMethod = ccl.getMethod("clear");
clearMethod.invoke(cache);
}
}
protected MessageFormat buildMessageFormat(String pattern, Locale locale) {
MessageFormatKey key = new MessageFormatKey(pattern, locale);
MessageFormat format = messageFormats.get(key);
if (format == null) {
format = new MessageFormat(pattern);
format.setLocale(locale);
format.applyPattern(pattern);
messageFormats.put(key, format);
}
return format;
}
protected String formatWithNullDetection(MessageFormat mf, Object[] args) {
String message = mf.format(args);
if ("null".equals(message)) {
return null;
} else {
return message;
}
}
@Inject(value = StrutsConstants.STRUTS_I18N_RELOAD, required = false)
public void setReloadBundles(String reloadBundles) {
this.reloadBundles = Boolean.parseBoolean(reloadBundles);
}
@Inject(value = StrutsConstants.STRUTS_DEVMODE, required = false)
public void setDevMode(String devMode) {
this.devMode = Boolean.parseBoolean(devMode);
}
/**
* Finds the given resource bundle by it's name.
* <p>
* Will use <code>Thread.currentThread().getContextClassLoader()</code> as the classloader.
* </p>
*
* @param aBundleName the name of the bundle (usually it's FQN classname).
* @param locale the locale.
* @return the bundle, <tt>null</tt> if not found.
*/
@Override
public ResourceBundle findResourceBundle(String aBundleName, Locale locale) {
ClassLoader classLoader = getCurrentThreadContextClassLoader();
String key = createMissesKey(String.valueOf(classLoader.hashCode()), aBundleName, locale);
if (missingBundles.contains(key)) {
return null;
}
ResourceBundle bundle = null;
try {
if (bundlesMap.containsKey(key)) {
bundle = bundlesMap.get(key);
} else {
bundle = ResourceBundle.getBundle(aBundleName, locale, classLoader);
bundlesMap.putIfAbsent(key, bundle);
}
} catch (MissingResourceException ex) {
if (delegatedClassLoaderMap.containsKey(classLoader.hashCode())) {
try {
if (bundlesMap.containsKey(key)) {
bundle = bundlesMap.get(key);
} else {
bundle = ResourceBundle.getBundle(aBundleName, locale, delegatedClassLoaderMap.get(classLoader.hashCode()));
bundlesMap.putIfAbsent(key, bundle);
}
} catch (MissingResourceException e) {
LOG.debug("Missing resource bundle [{}]!", aBundleName, e);
missingBundles.add(key);
}
} else {
LOG.debug("Missing resource bundle [{}]!", aBundleName);
missingBundles.add(key);
}
}
return bundle;
}
/**
* Creates a key to used for lookup/storing in the bundle misses cache.
*
* @param prefix the prefix for the returning String - it is supposed to be the ClassLoader hash code.
* @param aBundleName the name of the bundle (usually it's FQN classname).
* @param locale the locale.
* @return the key to use for lookup/storing in the bundle misses cache.
*/
private String createMissesKey(String prefix, String aBundleName, Locale locale) {
return prefix + aBundleName + "_" + locale.toString();
}
static class MessageFormatKey {
String pattern;
Locale locale;
MessageFormatKey(String pattern, Locale locale) {
this.pattern = pattern;
this.locale = locale;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MessageFormatKey that = (MessageFormatKey) o;
if (pattern != null ? !pattern.equals(that.pattern) : that.pattern != null) return false;
return locale != null ? locale.equals(that.locale) : that.locale == null;
}
@Override
public int hashCode() {
int result = pattern != null ? pattern.hashCode() : 0;
result = 31 * result + (locale != null ? locale.hashCode() : 0);
return result;
}
}
static class GetDefaultMessageReturnArg {
String message;
boolean foundInBundle;
public GetDefaultMessageReturnArg(String message, boolean foundInBundle) {
this.message = message;
this.foundInBundle = foundInBundle;
}
}
}
@@ -23,52 +23,32 @@ package com.opensymphony.xwork2.util;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.LocalizedTextProvider;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.reflection.ReflectionProviderFactory;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.struts2.StrutsConstants;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
/**
* Provides support for localization in the framework, it can be used to read only default bundles,
* or it can search the class hierarchy to find proper bundles.
*/
public class StrutsLocalizedTextProvider implements LocalizedTextProvider {
public class StrutsLocalizedTextProvider extends AbstractLocalizedTextProvider {
private static final Logger LOG = LogManager.getLogger(StrutsLocalizedTextProvider.class);
public static final String XWORK_MESSAGES_BUNDLE = "com/opensymphony/xwork2/xwork-messages";
public static final String STRUTS_MESSAGES_BUNDLE = "org/apache/struts2/struts-messages";
private static final String TOMCAT_RESOURCE_ENTRIES_FIELD = "resourceEntries";
private final ConcurrentMap<Integer, List<String>> classLoaderMap = new ConcurrentHashMap<>();
private boolean reloadBundles = false;
private boolean devMode = false;
private final ConcurrentMap<String, ResourceBundle> bundlesMap = new ConcurrentHashMap<>();
private final ConcurrentMap<MessageFormatKey, MessageFormat> messageFormats = new ConcurrentHashMap<>();
private final ConcurrentMap<Integer, ClassLoader> delegatedClassLoaderMap = new ConcurrentHashMap<>();
private final Set<String> missingBundles = Collections.synchronizedSet(new HashSet<String>());
private final String RELOADED = "com.opensymphony.xwork2.util.LocalizedTextUtil.reloaded";
/**
* Clears the internal list of resource bundles.
*
@@ -80,26 +60,10 @@ public class StrutsLocalizedTextProvider implements LocalizedTextProvider {
}
public StrutsLocalizedTextProvider() {
addDefaultResourceBundle(XWORK_MESSAGES_BUNDLE);
addDefaultResourceBundle(STRUTS_MESSAGES_BUNDLE);
}
/**
* Should resorce bundles be reloaded.
*
* @param reloadBundles reload bundles?
*/
@Inject(value = StrutsConstants.STRUTS_I18N_RELOAD, required = false)
public void setReloadBundles(String reloadBundles) {
this.reloadBundles = Boolean.parseBoolean(reloadBundles);
}
@Inject(value = StrutsConstants.STRUTS_DEVMODE, required = false)
public void setDevMode(String devMode) {
this.devMode = Boolean.parseBoolean(devMode);
}
@Inject(value = StrutsConstants.STRUTS_CUSTOM_I18N_RESOURCES, required = false)
public void setCustomI18NResources(String bundles) {
if (bundles != null && bundles.length() > 0) {
@@ -117,33 +81,6 @@ public class StrutsLocalizedTextProvider implements LocalizedTextProvider {
}
}
/**
* Add's the bundle to the internal list of default bundles.
* <p>
* If the bundle already exists in the list it will be readded.
* </p>
*
* @param resourceBundleName the name of the bundle to add.
*/
@Override
public void addDefaultResourceBundle(String resourceBundleName) {
//make sure this doesn't get added more than once
final ClassLoader ccl = getCurrentThreadContextClassLoader();
synchronized (XWORK_MESSAGES_BUNDLE) {
List<String> bundles = classLoaderMap.get(ccl.hashCode());
if (bundles == null) {
bundles = new CopyOnWriteArrayList<>();
classLoaderMap.put(ccl.hashCode(), bundles);
}
bundles.remove(resourceBundleName);
bundles.add(0, resourceBundleName);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Added default resource bundle '{}' to default resource bundles for the following classloader '{}'", resourceBundleName, ccl.toString());
}
}
/**
* Builds a {@link java.util.Locale} from a String of the form en_US_foo into a Locale
* with language "en", country "US" and variant "foo". This will parse the output of
@@ -189,135 +126,6 @@ public class StrutsLocalizedTextProvider implements LocalizedTextProvider {
return new Locale(language, country, localeStr);
}
/**
* Returns a localized message for the specified key, aTextName. Neither the key nor the
* message is evaluated.
*
* @param aTextName the message key
* @param locale the locale the message should be for
* @return a localized message based on the specified key, or null if no localized message can be found for it
*/
@Override
public String findDefaultText(String aTextName, Locale locale) {
List<String> localList = classLoaderMap.get(Thread.currentThread().getContextClassLoader().hashCode());
for (String bundleName : localList) {
ResourceBundle bundle = findResourceBundle(bundleName, locale);
if (bundle != null) {
reloadBundles();
try {
return bundle.getString(aTextName);
} catch (MissingResourceException e) {
// will be logged when not found in any bundle
}
}
}
if (devMode) {
LOG.warn("Missing key [{}] in bundles [{}]!", aTextName, localList);
} else {
LOG.debug("Missing key [{}] in bundles [{}]!", aTextName, localList);
}
return null;
}
/**
* Returns a localized message for the specified key, aTextName, substituting variables from the
* array of params into the message. Neither the key nor the message is evaluated.
*
* @param aTextName the message key
* @param locale the locale the message should be for
* @param params an array of objects to be substituted into the message text
* @return A formatted message based on the specified key, or null if no localized message can be found for it
*/
@Override
public String findDefaultText(String aTextName, Locale locale, Object[] params) {
String defaultText = findDefaultText(aTextName, locale);
if (defaultText != null) {
MessageFormat mf = buildMessageFormat(defaultText, locale);
return formatWithNullDetection(mf, params);
}
return null;
}
/**
* Finds the given resource bundle by it's name.
* <p>
* Will use <code>Thread.currentThread().getContextClassLoader()</code> as the classloader.
* </p>
*
* @param aBundleName the name of the bundle (usually it's FQN classname).
* @param locale the locale.
* @return the bundle, <tt>null</tt> if not found.
*/
@Override
public ResourceBundle findResourceBundle(String aBundleName, Locale locale) {
ClassLoader classLoader = getCurrentThreadContextClassLoader();
String key = createMissesKey(String.valueOf(classLoader.hashCode()), aBundleName, locale);
if (missingBundles.contains(key)) {
return null;
}
ResourceBundle bundle = null;
try {
if (bundlesMap.containsKey(key)) {
bundle = bundlesMap.get(key);
} else {
bundle = ResourceBundle.getBundle(aBundleName, locale, classLoader);
bundlesMap.putIfAbsent(key, bundle);
}
} catch (MissingResourceException ex) {
if (delegatedClassLoaderMap.containsKey(classLoader.hashCode())) {
try {
if (bundlesMap.containsKey(key)) {
bundle = bundlesMap.get(key);
} else {
bundle = ResourceBundle.getBundle(aBundleName, locale, delegatedClassLoaderMap.get(classLoader.hashCode()));
bundlesMap.putIfAbsent(key, bundle);
}
} catch (MissingResourceException e) {
LOG.debug("Missing resource bundle [{}]!", aBundleName, e);
missingBundles.add(key);
}
} else {
LOG.debug("Missing resource bundle [{}]!", aBundleName);
missingBundles.add(key);
}
}
return bundle;
}
/**
* @param classLoader a {@link ClassLoader} to look up the bundle from if none can be found on the current thread's classloader
*/
public void setDelegatedClassLoader(final ClassLoader classLoader) {
synchronized (bundlesMap) {
delegatedClassLoaderMap.put(getCurrentThreadContextClassLoader().hashCode(), classLoader);
}
}
/**
* @param bundleName Removes the bundle from any cached "misses"
*/
public void clearBundle(final String bundleName) {
bundlesMap.remove(getCurrentThreadContextClassLoader().hashCode() + bundleName);
}
/**
* Creates a key to used for lookup/storing in the bundle misses cache.
*
* @param prefix the prefix for the returning String - it is supposed to be the ClassLoader hash code.
* @param aBundleName the name of the bundle (usually it's FQN classname).
* @param locale the locale.
* @return the key to use for lookup/storing in the bundle misses cache.
*/
private String createMissesKey(String prefix, String aBundleName, Locale locale) {
return prefix + aBundleName + "_" + locale.toString();
}
/**
* Calls {@link #findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args)}
* with aTextName as the default message.
@@ -759,28 +567,6 @@ public class StrutsLocalizedTextProvider implements LocalizedTextProvider {
}
}
private String formatWithNullDetection(MessageFormat mf, Object[] args) {
String message = mf.format(args);
if ("null".equals(message)) {
return null;
} else {
return message;
}
}
private MessageFormat buildMessageFormat(String pattern, Locale locale) {
MessageFormatKey key = new MessageFormatKey(pattern, locale);
MessageFormat format = messageFormats.get(key);
if (format == null) {
format = new MessageFormat(pattern);
format.setLocale(locale);
format.applyPattern(pattern);
messageFormats.put(key, format);
}
return format;
}
/**
* Traverse up class hierarchy looking for message. Looks at class, then implemented interface,
* before going up hierarchy.
@@ -790,7 +576,7 @@ public class StrutsLocalizedTextProvider implements LocalizedTextProvider {
private String findMessage(Class clazz, String key, String indexedKey, Locale locale, Object[] args, Set<String> checked,
ValueStack valueStack) {
if (checked == null) {
checked = new TreeSet<String>();
checked = new TreeSet<>();
} else if (checked.contains(clazz.getName())) {
return null;
}
@@ -849,85 +635,6 @@ public class StrutsLocalizedTextProvider implements LocalizedTextProvider {
return null;
}
private void reloadBundles() {
reloadBundles(ActionContext.getContext() != null ? ActionContext.getContext().getContextMap() : null);
}
private void reloadBundles(Map<String, Object> context) {
if (reloadBundles) {
try {
Boolean reloaded;
if (context != null) {
reloaded = (Boolean) ObjectUtils.defaultIfNull(context.get(RELOADED), Boolean.FALSE);
}else {
reloaded = Boolean.FALSE;
}
if (!reloaded) {
bundlesMap.clear();
try {
clearMap(ResourceBundle.class, null, "cacheList");
} catch (NoSuchFieldException e) {
// happens in IBM JVM, that has a different ResourceBundle impl
// it has a 'cache' member
clearMap(ResourceBundle.class, null, "cache");
}
// now, for the true and utter hack, if we're running in tomcat, clear
// it's class loader resource cache as well.
clearTomcatCache();
if(context!=null) {
context.put(RELOADED, true);
}
LOG.debug("Resource bundles reloaded");
}
} catch (Exception e) {
LOG.error("Could not reload resource bundles", e);
}
}
}
private void clearTomcatCache() {
ClassLoader loader = getCurrentThreadContextClassLoader();
// no need for compilation here.
Class cl = loader.getClass();
try {
if ("org.apache.catalina.loader.WebappClassLoader".equals(cl.getName())) {
clearMap(cl, loader, TOMCAT_RESOURCE_ENTRIES_FIELD);
} else {
LOG.debug("Class loader {} is not tomcat loader.", cl.getName());
}
} catch (NoSuchFieldException nsfe) {
if ("org.apache.catalina.loader.WebappClassLoaderBase".equals(cl.getSuperclass().getName())) {
LOG.debug("Base class {} doesn't contain '{}' field, trying with parent!", cl.getName(), TOMCAT_RESOURCE_ENTRIES_FIELD, nsfe);
try {
clearMap(cl.getSuperclass(), loader, TOMCAT_RESOURCE_ENTRIES_FIELD);
} catch (Exception e) {
LOG.warn("Couldn't clear tomcat cache using {}", cl.getSuperclass().getName(), e);
}
}
} catch (Exception e) {
LOG.warn("Couldn't clear tomcat cache", cl.getName(), e);
}
}
private void clearMap(Class cl, Object obj, String name)
throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Field field = cl.getDeclaredField(name);
field.setAccessible(true);
Object cache = field.get(obj);
synchronized (cache) {
Class ccl = cache.getClass();
Method clearMethod = ccl.getMethod("clear");
clearMethod.invoke(cache);
}
}
/**
* Clears all the internal lists.
*
@@ -938,63 +645,4 @@ public class StrutsLocalizedTextProvider implements LocalizedTextProvider {
// no-op
}
static class MessageFormatKey {
String pattern;
Locale locale;
MessageFormatKey(String pattern, Locale locale) {
this.pattern = pattern;
this.locale = locale;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MessageFormatKey)) return false;
final MessageFormatKey messageFormatKey = (MessageFormatKey) o;
if (locale != null ? !locale.equals(messageFormatKey.locale) : messageFormatKey.locale != null)
return false;
if (pattern != null ? !pattern.equals(messageFormatKey.pattern) : messageFormatKey.pattern != null)
return false;
return true;
}
@Override
public int hashCode() {
int result;
result = (pattern != null ? pattern.hashCode() : 0);
result = 29 * result + (locale != null ? locale.hashCode() : 0);
return result;
}
}
private static ClassLoader getCurrentThreadContextClassLoader() {
return Thread.currentThread().getContextClassLoader();
}
static class GetDefaultMessageReturnArg {
String message;
boolean foundInBundle;
public GetDefaultMessageReturnArg(String message, boolean foundInBundle) {
this.message = message;
this.foundInBundle = foundInBundle;
}
}
private static class EmptyResourceBundle extends ResourceBundle {
@Override
public Enumeration<String> getKeys() {
return null; // dummy
}
@Override
protected Object handleGetObject(String key) {
return null; // dummy
}
}
}