Details
-
Suggestion
-
Resolution: Fixed
Description
WebWork used strange way to find action - it ask for action handler and look on result - if Exception, it will try to get another action. On everyone request milliards of Exceptions throws for everyone action. We have a patch for it - just caching valid action handling for future get it without exceptions waterfall.
Look into webwork.properties and replace webwork.configuration.class for Cached configurations:
#webwork.configuration.class=com.atlassian.jira.config.webwork.ApplicationPropertiesConfiguration,com.atlassian.jira.config.webwork.JiraConfiguration webwork.configuration.class=com.atlassian.jira.config.webwork.CachedApplicationPropertiesConfiguration,com.atlassian.jira.config.webwork.CachedJiraConfiguration
Sure, you must implement it:
package com.atlassian.jira.config.webwork; import com.atlassian.jira.config.webwork.ApplicationPropertiesConfiguration; public class CachedApplicationPropertiesConfiguration extends ApplicationPropertiesConfiguration implements DelegatedConfiguration { private final WebworkConfigurationCache config = new WebworkConfigurationCache(this); @Override public Object getImpl(String aName) throws IllegalArgumentException { return config.getImpl(aName); } @Override public void setImpl(String aName, Object aValue) throws IllegalArgumentException, UnsupportedOperationException { config.setImpl(aName, aValue); } public Object getDelegateImpl(String aName) { return super.getImpl(aName); } public void setDelegateImpl(String aName, Object aValue) { super.setImpl(aName, aValue); } }
package com.atlassian.jira.config.webwork; import com.atlassian.jira.config.webwork.JiraConfiguration; public class CachedJiraConfiguration extends JiraConfiguration implements DelegatedConfiguration { private final WebworkConfigurationCache cache = new WebworkConfigurationCache(this); @Override public Object getImpl(String aName) throws IllegalArgumentException { return cache.getImpl(aName); } @Override public void setImpl(String aName, Object aValue) throws IllegalArgumentException, UnsupportedOperationException { cache.setImpl(aName, aValue); } public Object getDelegateImpl(String aName) { return super.getImpl(aName); } public void setDelegateImpl(String aName, Object aValue) { super.setImpl(aName, aValue); } }
package com.atlassian.jira.config.webwork; public interface DelegatedConfiguration { Object getDelegateImpl(String aName); void setDelegateImpl(String aName, Object aValue); }
package com.atlassian.jira.config.webwork; import java.lang.ref.SoftReference; import java.util.*; import org.apache.log4j.Logger; /** * Configuration cache to increase webwork configuration. * This method cache all requests to webwork config. The webwork config is used * exception flow for "no value" mark. But it bottle neck for JIRA. * * @author Alexey Efimov */ public class WebworkConfigurationCache { private static final List<String> NULLABLE = Arrays.asList( "webwork.util.SubsetIteratorFilter.success", "util.UserAction.success" ); private static final String WEBWORK_ACTION_EXTENSION = "webwork.action.extension"; private static final String DEFAULT_WEBWORK_ACTION_EXTENSION_VALUE = "jspa"; private static final String WEBWORK_CONFIGURATION_XML_RELOAD = "webwork.configuration.xml.reload"; private static final String DEFAULT_WEBWORK_CONFIGURATION_XML_RELOAD_VALUE = Boolean.FALSE.toString(); private static final Logger LOG = Logger.getLogger(WebworkConfigurationCache.class); private final DelegatedConfiguration delegate; private final Cache[] cache = {new Cache(), new Cache()}; public WebworkConfigurationCache(DelegatedConfiguration delegate) { this.delegate = delegate; } public Object getImpl(String aName) throws IllegalArgumentException { if (contains(aName)) { return get(aName); } try { return getAndCache(aName); } catch (IllegalArgumentException e) { // This is root of really low performance (see webwork.config.XMLActionConfiguration.getImpl) if (WEBWORK_CONFIGURATION_XML_RELOAD.equals(aName)) { cache(aName, DEFAULT_WEBWORK_CONFIGURATION_XML_RELOAD_VALUE); } else if (WEBWORK_ACTION_EXTENSION.equals(aName)) { cache(aName, DEFAULT_WEBWORK_ACTION_EXTENSION_VALUE); } else if (NULLABLE.contains(aName)) { // This is an approved nullable cache cache(aName, null); } throw e; } } private Object get(String aName) { return cache[cache.length - 1].get(aName); } private boolean contains(String aName) { return cache[cache.length - 1].contains(aName); } private Object getAndCache(String aName) { Object impl = delegate.getDelegateImpl(aName); cache(aName, impl); return impl; } public void setImpl(String aName, Object aValue) throws IllegalArgumentException, UnsupportedOperationException { delegate.setDelegateImpl(aName, aValue); cache(aName, aValue); } private Object cache(String aName, Object aValue) { // Check existing levels of cache int start = 0; while (start < cache.length && cache[start].same(aName, aValue)) { start++; } // Fill first empty level if (start < cache.length) { if (start == cache.length - 1) { // Only last level is used for reading from cache LOG.info(String.format("Put to cache key %s and value %s", aName, aValue)); } cache[start].set(aName, aValue); } // And clear last for (int i = start + 1; i < cache.length; i++) { cache[i].remove(aName); } return aValue; } private static final class Cache { public final Set<String> nullCache = new HashSet<String>(); public final Map<String, SoftReference<Object>> cache = new HashMap<String, SoftReference<Object>>(); public boolean contains(String key) { if (nullCache.contains(key)) { return true; } SoftReference<Object> reference = cache.get(key); if (reference != null) { Object v = reference.get(); if (v != null) { return true; } } return false; } public boolean same(String key, Object value) { if (contains(key)) { Object v = get(key); return value == null && v == null || value != null && value.equals(v); } return false; } public Object get(String key) { if (nullCache.contains(key)) { return null; } SoftReference<Object> reference = cache.get(key); if (reference != null) { Object v = reference.get(); if (v != null) { return v; } } return null; } public void set(String key, Object value) { if (value != null) { nullCache.remove(key); cache.put(key, new SoftReference<Object>(value)); } else { cache.remove(key); nullCache.add(key); } } public void remove(String key) { nullCache.remove(key); cache.remove(key); } } }